--- --- Splines and Generalized Additive Models

Goals for Today

  1. Walk away understanding how splines are constructed and fit.
  2. Start to understand some of the strengths and weaknesses of non-linear modelling.
  3. Understand the basics of fitting a generalized additive model in R.

Where would this sit in a full course?

Before today

We couldn’t really jump into this content on day one of a course. Since fitting, analyzing, and visualizing generalized additive models is all done in R, the preceding weeks would be dedicated to teaching the basics of R programming, data wrangling and visualization, and I would probably utilize some revised version of these materials from my 2017 LSA Institute course for that:

Similarly, we’d also want to lay some foundation in statistical analysis and regression modelling. For that, we’d either continue on with my Institute materials, or Danielle Navarro’s much praised Learning Statistics with R.

After today

After understanding the basics how how splines work, and how we fit them using generalized additive models, topics for future meetings would be about

  • how to interpret the results of the model (more complex models = harder to interpret)
  • how to visualize the model fits
  • how to complexify our models in important ways (random effects, autocorrelation)
  • how to get interesting derivative information from the model (like, rate of change, acceleration of change)

Why Use Splines?

Straight Lines Are Simple

The most common kind of modelling technique we use when we have data to analyze is a linear regression. For example, here’s two linear regressions that are modelling the relationship between date of birth and normalized F1 (aka vowel height) for two allophones of /ay/ in Philadelphia.

This approach is useful, because first off, these straight lines don’t look to correct, and they boil the complex social and mathematical relationships involved in the actual data down to just two numbers:

  1. The intercept of the line (the \(y\) value when \(x=0\)).
  2. The slope (the degree to which \(y\) changes for every increase of 1 for \(x\)).

Two numbers are easy to look at, and easy to understand. It’s also the case that if the data was just a little bit different, these numbers wouldn’t change very much.1

The linear modelling approach is also ubiquitous. Every fancy or state of the art elaboration on linear modelling is, at its core, still trying to describe the relationship between variables in terms of intercepts and slopes, including:

  • logistic regression
  • poisson regression
  • zero inflated poisson regression
  • mixed effects models

More or less, my advice would if you can get away with using a linear regression or some fancier version of it, go for it.

Straight lines are too simple sometimes

Sometimes the kind of data we’re working with is obviously more complex than could reasonably be modeled using straight lines. Here’s a plot of the formant trajectory of one token of [ay] moving through the F1xF2 space, and below that is just the F1 trajectory of [ay] over time.

We could try to model these trajectories using straight lines. But if we do this on a subset of /ay/ formant track data from a subset of people in the PNC, the linear regression lines in blue are losing some important information that we can see is present in the data.

What we want is some way of characterizing an arbitrary curvy line like the single F1 formant track below, using just a few numbers, in a way that is (relatively) easy to interpret and extrapolate from.

One way you might have seen of doing this is utilizing “polynomial” regression.

model <- lm(F1 ~ t_prop + I(t_prop^2) + I(t_prop^3), data = ay_single)

This gets closer to fitting the data, but the big problem here is understanding what on earth \(t^3\) means!

“Basis” functions

One way to approximate an arbitrary curve with a small number of coefficients is to use a “basis” function. The basis function is a collection of curves that you can scale, then sum over, to approximate the curve of interest. If you’ve ever taken intro to phonetics, you’re actually already familiar with one basis function, even if you’re not aware of it!

The Fourier Transform!

What is the curve of greatest interest to linguists if not a waveform?

This waveform is from a very short clip (16 milliseconds) of me saying [i]. Just this very short clip has 742 data points (or samples) in it. But we can approximate the waveform with a much smaller number of values using the Fourier transform.

This is an example of a very small Fourier basis. It’s a collection of 4 sine and cosine curves across the interval of interest. One thing you can see is that each next curve is oscillating at a higher frequency. One of them only has one peak in the interval, while the next has two, then three, etc.

The way you approximate an arbitrary curve with this collection of curves is by scaling each one. That is, you make it bigger or smaller by multiplying it by some number. Then, you scan across the x axis, summing up the values to get the estimate for the curve.

Now, 4 curves are not enough to capture the complexity of the waveform above. With a larger number of curves, each oscillating at a faster and faster rate, we can get closer. For example, the approximation we get with a Fourier basis of 15 curves is pretty close, but still over-smoothing it.

Here’s an animation of how much closer and closer our model can approximate the waveform as we increase the number of curves in the basis.

This illustrates an important point that we need to take seriously when we’re fitting our own models for real:

Bigger Basis = More Wiggly

The next kinda fun thing to look at is the actual weights that we’re using to scale each curve in the Fourier Basis. Here’s a little plot of all of the coefficients, color coded black for the biggest values and white for smallest values, stacked on top of eachother in a single column.

If we did a bit more math, and then did this same curve fitting process in a rolling window across an entire audio file, and at each next window placed the column of coefficients for each window next to the one before, the graph would start to look like something pretty familiar.

If we come back to our original question about how to approximate a curve matching the F1 trajectory of [ay] above, we can actually also use a Fourier basis! The Fourier transform can be used to described any curve.

However, the Fourier basis is particularly good for waveforms becuase we know that they consist of oscillating curves, and the fourier basis is made up of oscillating curves. This means we can actually look at the coefficients from the Fourier transform and they’ll have some kind of meaning to us. This isn’t true for most curves we’re interested in, though, like the F1 formant trajectory. In their 2006 book on Functional Data Analysis, Ramsay & Silverman say

A Fourier series is like margarine: It’s cheap and you can spread it on practically anything, but don’t expect that the result will be exciting eating.

Splines, a.k.a. Rosemary Truffle Aioli

There are lots of different kinds of splines, but they all basically look like some variant of this:

Just like we saw before, we can re-scale each of these curves then sum over them to get some new estimated curve.

Here’s a little animation showing a range of different curves we can get out of a b-spline basis this size:

And it’s also important to also remember:

Bigger Basis = More Wiggly

Knots

Now is the time to introduce a new notion: knots. You’ll most often see people talking about splines in terms of “knots” or “control points”. If you’ve everplayed around with making vector graphics in something like Photoshop, Illustrator, or Inkscape, you’ll be kind of familiar with with the notion of “knots”. Here’s a figure of a line I drew in Inkscape that has one knot.

The relationship between the number of “knots” and the number of basis functions is a little bit complicated2, but usually the more knots, the more basis polynomials there are, so we can now further revise our saying about wiggliness.

More Knots = More Wiggly

The basis we were looking at above actually had only one knot, but even with that we can fit the curve of the that single formant track really well.

Interpretation… not so straightforward

The downside to splines is that the coefficients we get for each polynomial aren’t really interpretable. Here are the 5 coefficients that are being used to fit the curve to the formant trajectory.

      X1       X2       X3       X4       X5 
743.0011 817.7983 820.2886 585.1586 593.9880 

These don’t really mean anything like the fourier transform coefficients did. Interpreting the results of fitting a model with splines will take a little bit more work. For the most part, it will involve making visualizations of the data.ah

The Math Basics

Here are two questions that are pretty reasionable right now:

  1. Ok, how do i get these curves?
  2. Isn’t multiplying and adding all these curves together a lot of work?

The answers are “the computer gives them to us” and “surprisingly no!”

For the most part, we’re not going to be constructing spline bases “by hand” (see the “Doing it in R” section below). But even then it’s not that tricky. The splines package will let us construct a b-spline basis as easilly as this:

library(splines)
bspline_basis <- bs(1:10, df = 5, intercept = T)
bspline_basis

Now lets say we choose the following 5 coefficients

coefficients <- c(3, 6, 2, 5, 1)

Scaling each polynomial and summing up across them is as easy as:

coefficients %*% t(bspline_basis)
     [,1]     [,2]    [,3]     [,4]     [,5]     [,6]     [,7]     [,8]     [,9] [,10]
[1,]    3 4.333333 4.62963 4.333333 3.888889 3.721536 3.814815 3.710562 2.931413     1

The %*% syntax is doing matrix multiplication. Usually you can get away without thinking about matrix multiplicatio or linear algebra when doing stats, but since it underlies so many quantitative and computational methods, it’s handy to know a little bit about it.

This animation illustrates the mathematical operations that are going on when you do the matrix multiplication written out above.

Doing it in R

To fit a spline to data in R, you’ll want to use the gam() function, which stands for “generalized additive model” from the mgcv package. Let’s remind ourselves of the [ay] trajectory data.

Let’s fit a generalized additive model to this data, with the following specifications.

  • We want to enter gender into the model like we would for any linear model.
  • We want to fit a spline over t_prop with, let’s say, 10 knots.
  • We want to fit separate splines for each gender.

Here’s the code:

library(mgcv)
ay_model <- gam(F1 ~ gender + s(t_prop, by = gender, k = 10), 
                data = ay_trajectory)

Now, gam is doing a lot more fancy stuff than just finding the best coefficients for each basis polynomial. For one, it’s automatically identifying and utilizing a “smoothing parameter.” As the smoothing parameter increases, it’ll penalize the curve down to a straight line. What’s nice about this is that the resulting smooth won’t (usually) be unduely wiggly, even if you have many many knots.

If we look at a summary of ay_model, there’s some useful information, but it’s less interpretable than a normal linear regression.

summary(ay_model)

Family: gaussian 
Link function: identity 

Formula:
F1 ~ gender + s(t_prop, by = gender, k = 10)

Parametric coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  519.706      3.802  136.68   <2e-16 ***
genderwomen  127.337      5.510   23.11   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Approximate significance of smooth terms:
                        edf Ref.df     F p-value    
s(t_prop):gendermen   2.899  3.596 35.03  <2e-16 ***
s(t_prop):genderwomen 3.374  4.172 48.22  <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

R-sq.(adj) =  0.733   Deviance explained = 73.9%
GCV = 2449.7  Scale est. = 2385.4    n = 315

The significant gender effect tells us that, unsurprisingly, women had a higher F1 than men by about 127 Hz. The significance of the smooth terms isn’t all that meaningful: it means the smooths were significantly different than a straight flat line. The column called edf (stands for Estimated Degrees of Freedom) is useful to eyeball. If the smooth was penalized down to a straight line, it would have an an EDF of about 1. These EDFs are larger than 1, and women’s are larger than men’s (although, we’d need to do a lot more work to see if it was significantly larger), so it looks like it was worth fitting a fanicer model than just a linear model!

To really explore gam fits, we have to plot them. I’m going to spare you the code for making these plots right now, but you can scrutinize it by downloading the code from the little drop down button at the top of the page.

But here’s an important warning!

Don’t just look to see if the confidence intervals of the smooths are overlapping or not!

You can’t really really tell if two curves are significantly different from just looking at whether their confidence intervals overlap, cause that’s not really how confidence intervals work. Instead, we need to look at a difference curve, which we can get from a gam model.

Recap, and Further Issues

Recap

  1. Splines are fit using a “basis” of polynomial curves which are scaled and summed to approximate a curve.
  2. One of the strengths of using non-linear methods is that you can model relationships that are clearly non linear. Some weaknesses is that it is more challenging to intepret the results, and we need to be careful of over-fitting.
  3. You can fit a spline using generalized additive models, specifically as implemented in the mgcv package.

Things you’ll want gams to do, and they can

Random Intercepts

Each speaker has their own personal average F1 value for their [ay] that won’t be perfectly captured by their demographic features. For this, you’ll want to fit random intercepts by speaker, and you can.

Random smooths

Each speaker will also have their own personal F1 trajectories, and to capture this, you’ll want to fit random smooths (like random slopes), and you can.

Autocorrelation

Importantly, the data we’re modelling aren’t just a random association of data points between time stamps and F1 values, like this plot shows.

Each F1 value is directly connected to an F1 value at the next time point. That is, they are auto-correlated.

You can capture this with gams also!

Multidimensional Smooths

The most fun thing to do with gams is to fit multidimensional smooths. For example, how does the trajectory of F1 change over dates of birth? I’ve included the code for that model here, just so you can see how simple an extension it is of what we’ve already done.

big_ay_model <- gam(F1 ~ te(t_prop, dob, k = 10)+
                       s(idstring, bs = 're'), 
                     data = ay0_m)


  1. It has low variance, but high bias.

  2. The number of basis functions is = knots + degree - 2. The “degree” of the basis functions controls how the curves connect smoothly at the knots. A degree of 3 makes the rate of change smooth, and a degree of 4 also makes the acceleration smooth. A degree of 3 or 4 is usually what you’ll want to be working with.

LS0tCnRpdGxlOiAiU3BsaW5lcyBhbmQgR2VuZXJhbGl6ZWQgQWRkaXRpdmUgTW9kZWxzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgY29kZV9mb2xkaW5nOiBub25lCiAgICBjc3M6IGN1c3RvbS5jc3MKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiAzCi0tLQoKYGBge3IgZWNobyA9IEZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNwbGluZXMpCmxpYnJhcnkodHVuZVIpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkoZ2dhbmltYXRlKQpsaWJyYXJ5KGZkYSkKYGBgCgoKIyBHb2FscyBmb3IgVG9kYXkKCjEuIFdhbGsgYXdheSB1bmRlcnN0YW5kaW5nIGhvdyBzcGxpbmVzIGFyZSBjb25zdHJ1Y3RlZCBhbmQgZml0LgoyLiBTdGFydCB0byB1bmRlcnN0YW5kIHNvbWUgb2YgdGhlIHN0cmVuZ3RocyBhbmQgd2Vha25lc3NlcyBvZiBub24tbGluZWFyIG1vZGVsbGluZy4KMy4gVW5kZXJzdGFuZCB0aGUgYmFzaWNzIG9mIGZpdHRpbmcgYSBnZW5lcmFsaXplZCBhZGRpdGl2ZSBtb2RlbCBpbiBSLgoKIyMgV2hlcmUgd291bGQgdGhpcyBzaXQgaW4gYSBmdWxsIGNvdXJzZT8KCiMjIyBCZWZvcmUgdG9kYXkKCldlIGNvdWxkbid0IHJlYWxseSBqdW1wIGludG8gdGhpcyBjb250ZW50IG9uIGRheSBvbmUgb2YgYSBjb3Vyc2UuIFNpbmNlIGZpdHRpbmcsIGFuYWx5emluZywgYW5kIHZpc3VhbGl6aW5nIGdlbmVyYWxpemVkIGFkZGl0aXZlIG1vZGVscyBpcyBhbGwgZG9uZSBpbiBSLCB0aGUgcHJlY2VkaW5nIHdlZWtzIHdvdWxkIGJlIGRlZGljYXRlZCB0byB0ZWFjaGluZyB0aGUgYmFzaWNzIG9mIFIgcHJvZ3JhbW1pbmcsIGRhdGEgd3JhbmdsaW5nIGFuZCB2aXN1YWxpemF0aW9uLCBhbmQgSSB3b3VsZCBwcm9iYWJseSB1dGlsaXplIHNvbWUgcmV2aXNlZCB2ZXJzaW9uIG9mIHRoZXNlIG1hdGVyaWFscyBmcm9tIG15IDIwMTcgTFNBIEluc3RpdHV0ZSBjb3Vyc2UgZm9yIHRoYXQ6Cgo8ZGl2IHN0eWxlPSJ3aWR0aDoxMDAlO2Zsb2F0OmxlZnQ7Ij4KPGRpdiBjbGFzcyA9ICJib3ggc2hlZXQiIHN0eWxlID0gIndpZHRoOjIwJTtmbG9hdDpsZWZ0O21hcmdpbnM6YXV0bzsiPgoKWyFbXShmaWd1cmVzL3dlZWsxLnBuZyldKGh0dHBzOi8vam9mcmh3bGQuZ2l0aHViLmlvL3RlYWNoaW5nL2NvdXJzZXMvMjAxN19sc2EvbGVjdHVyZXMvU2Vzc2lvbl8xLm5iLmh0bWwpCgo8L2Rpdj4KCjxkaXYgY2xhc3MgPSAiYm94IHNoZWV0IiBzdHlsZSA9ICJ3aWR0aDoyMCU7ZmxvYXQ6bGVmdDttYXJnaW5zOmF1dG87Ij4KClshW10oZmlndXJlcy93ZWVrMi5wbmcpXShodHRwczovL2pvZnJod2xkLmdpdGh1Yi5pby90ZWFjaGluZy9jb3Vyc2VzLzIwMTdfbHNhL2xlY3R1cmVzL1Nlc3Npb25fMi5uYi5odG1sKQoKPC9kaXY+CjxkaXYgY2xhc3MgPSAiYm94IHNoZWV0IiBzdHlsZSA9ICJ3aWR0aDoyMCU7ZmxvYXQ6bGVmdDttYXJnaW5zOmF1dG87Ij4KClshW10oZmlndXJlcy93ZWVrMy5wbmcpXShodHRwczovL2pvZnJod2xkLmdpdGh1Yi5pby90ZWFjaGluZy9jb3Vyc2VzLzIwMTdfbHNhL2xlY3R1cmVzL1Nlc3Npb25fMy5uYi5odG1sKQoKPC9kaXY+CjxkaXYgY2xhc3MgPSAiYm94IHNoZWV0IiBzdHlsZSA9ICJ3aWR0aDoyMCU7ZmxvYXQ6bGVmdDttYXJnaW5zOmF1dG87Ij4KClshW10oZmlndXJlcy93ZWVrNC5wbmcpXShodHRwczovL2pvZnJod2xkLmdpdGh1Yi5pby90ZWFjaGluZy9jb3Vyc2VzLzIwMTdfbHNhL2xlY3R1cmVzL1Nlc3Npb25fNC5uYi5odG1sKQoKPC9kaXY+CjwvZGl2PgoKU2ltaWxhcmx5LCB3ZSdkIGFsc28gd2FudCB0byBsYXkgKnNvbWUqIGZvdW5kYXRpb24gaW4gc3RhdGlzdGljYWwgYW5hbHlzaXMgYW5kIHJlZ3Jlc3Npb24gbW9kZWxsaW5nLiBGb3IgdGhhdCwgd2UnZCBlaXRoZXIgY29udGludWUgb24gd2l0aCBteSBJbnN0aXR1dGUgbWF0ZXJpYWxzLCBvciBEYW5pZWxsZSBOYXZhcnJvJ3MgbXVjaCBwcmFpc2VkICpMZWFybmluZyBTdGF0aXN0aWNzIHdpdGggUiouCgo8ZGl2IHN0eWxlPSJ3aWR0aDoxMDAlO2Zsb2F0OmxlZnQ7Ij4KPGRpdiBjbGFzcyA9ICJib3ggc2hlZXQiIHN0eWxlID0gIndpZHRoOjIwJTtmbG9hdDpsZWZ0O21hcmdpbnM6YXV0bzsiPgoKWyFbXShmaWd1cmVzL3dlZWs1LnBuZyldKGh0dHBzOi8vam9mcmh3bGQuZ2l0aHViLmlvL3RlYWNoaW5nL2NvdXJzZXMvMjAxN19sc2EvbGVjdHVyZXMvU2Vzc2lvbl81Lm5iLmh0bWwpCgo8L2Rpdj4KCjxkaXYgY2xhc3MgPSAiYm94IHNoZWV0IiBzdHlsZSA9ICJ3aWR0aDoyMCU7ZmxvYXQ6bGVmdDttYXJnaW5zOmF1dG87Ij4KClshW10oZmlndXJlcy93ZWVrNi5wbmcpXShodHRwczovL2pvZnJod2xkLmdpdGh1Yi5pby90ZWFjaGluZy9jb3Vyc2VzLzIwMTdfbHNhL2xlY3R1cmVzL1Nlc3Npb25fNi5uYi5odG1sKQoKPC9kaXY+CjxkaXYgY2xhc3MgPSAiYm94IHNoZWV0IiBzdHlsZSA9ICJ3aWR0aDoyMCU7ZmxvYXQ6bGVmdDttYXJnaW5zOmF1dG87Ij4KClshW10oZmlndXJlcy93ZWVrNy5wbmcpXShodHRwczovL2pvZnJod2xkLmdpdGh1Yi5pby90ZWFjaGluZy9jb3Vyc2VzLzIwMTdfbHNhL2xlY3R1cmVzL1Nlc3Npb25fNy5uYi5odG1sKQoKPC9kaXY+CjxkaXYgY2xhc3MgPSAiYm94IHNoZWV0IiBzdHlsZSA9ICJ3aWR0aDoyMCU7ZmxvYXQ6bGVmdDttYXJnaW5zOmF1dG87Ij4KClshW10oZmlndXJlcy93ZWVrOC5wbmcpXShodHRwczovL2pvZnJod2xkLmdpdGh1Yi5pby90ZWFjaGluZy9jb3Vyc2VzLzIwMTdfbHNhL2xlY3R1cmVzL1Nlc3Npb25fOC5uYi5odG1sKQoKPC9kaXY+CjwvZGl2PgoKCjxkaXYgc3R5bGU9IndpZHRoOjEwMCU7ZmxvYXQ6bGVmdDsiPgo8ZGl2IGNsYXNzID0gImJveCBzaGVldCIgc3R5bGUgPSAid2lkdGg6MjAlO2Zsb2F0OmxlZnQ7bW1hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KClshW10oZmlndXJlcy9sc3dyLnBuZyldKGh0dHBzOi8vbGVhcm5pbmdzdGF0aXN0aWNzd2l0aHIuY29tKQoKPC9kaXY+CjwvZGl2PgoKIyMjIEFmdGVyIHRvZGF5CgpBZnRlciB1bmRlcnN0YW5kaW5nIHRoZSBiYXNpY3MgaG93IGhvdyBzcGxpbmVzIHdvcmssIGFuZCBob3cgd2UgZml0IHRoZW0gdXNpbmcgZ2VuZXJhbGl6ZWQgYWRkaXRpdmUgbW9kZWxzLCB0b3BpY3MgZm9yIGZ1dHVyZSBtZWV0aW5ncyB3b3VsZCBiZSBhYm91dAoKLSBob3cgdG8gaW50ZXJwcmV0IHRoZSByZXN1bHRzIG9mIHRoZSBtb2RlbCAobW9yZSBjb21wbGV4IG1vZGVscyA9IGhhcmRlciB0byBpbnRlcnByZXQpCi0gaG93IHRvIHZpc3VhbGl6ZSB0aGUgbW9kZWwgZml0cwotIGhvdyB0byBjb21wbGV4aWZ5IG91ciBtb2RlbHMgaW4gaW1wb3J0YW50IHdheXMgKHJhbmRvbSBlZmZlY3RzLCBhdXRvY29ycmVsYXRpb24pCi0gaG93IHRvIGdldCBpbnRlcmVzdGluZyBkZXJpdmF0aXZlIGluZm9ybWF0aW9uIGZyb20gdGhlIG1vZGVsIChsaWtlLCByYXRlIG9mIGNoYW5nZSwgYWNjZWxlcmF0aW9uIG9mIGNoYW5nZSkKCgojIFdoeSBVc2UgU3BsaW5lcz8KCiMjIFN0cmFpZ2h0IExpbmVzIEFyZSBTaW1wbGUKClRoZSBtb3N0IGNvbW1vbiBraW5kIG9mIG1vZGVsbGluZyB0ZWNobmlxdWUgd2UgdXNlIHdoZW4gd2UgaGF2ZSBkYXRhIHRvIGFuYWx5emUgaXMgYSAqbGluZWFyIHJlZ3Jlc3Npb24qLgpGb3IgZXhhbXBsZSwgaGVyZSdzIHR3byBsaW5lYXIgcmVncmVzc2lvbnMgdGhhdCBhcmUgbW9kZWxsaW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkYXRlIG9mIGJpcnRoIGFuZCBub3JtYWxpemVkIEYxIChha2Egdm93ZWwgaGVpZ2h0KSBmb3IgdHdvIGFsbG9waG9uZXMgb2YgL2F5LyBpbiBQaGlsYWRlbHBoaWEuCgo8ZGl2IHN0eWxlID0gIndpZHRoOjc1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CmBgYHtyIGZpZy53aWR0aD0yLjUsIGZpZy5oZWlnaHQgPSAxLjUsIGVjaG8gPSBGLCBvdXQud2lkdGggPSAiMTAwJSJ9CmFsbF9tZWFzX2RmIDwtIHJlYWRfcmRzKCJkYXRhL2FsbF9tZWFzX2RmLnJkcyIpCmF5cyA8LSBhbGxfbWVhc19kZiAlPiUgZmlsdGVyKHBsdF92Y2xhc3MgJWluJSBjKCJheTAiLCAiYXkiKSkKYXlzICU+JQogIG11dGF0ZShkb2IgPSB5ZWFyLWFnZSkgJT4lCiAgZ3JvdXBfYnkoaWRzdHJpbmcsIHBsdF92Y2xhc3MsIGRvYikgJT4lCiAgc3VtbWFyaXNlKEYxX24gPSBtZWFuKEYxX24pKS0+YXlfbWVhbnMKYXlfbWVhbnMgJT4lCmdncGxvdChhZXMoZG9iLCBGMV9uLCBjb2xvciA9IHBsdF92Y2xhc3MpKSsKICBnZW9tX3BvaW50KGFscGhhID0gMC40KSsKICBzY2FsZV95X3JldmVyc2UoKSsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKSsKICBzY2FsZV9jb2xvcl9jb2xvcmJsaW5kKCJ2b3dlbCIsIGxpbWl0cyA9IGMoImF5MCIsICJheSIpLCBsYWJlbHMgPSBjKCJheVQiLCAiYXlWIikpKwogIHlsYWIoIm5vcm1hbGl6ZWQgRjEiKSsKICB4bGFiKCJkYXRlIG9mIGJpcnRoIikrCiAgdGhlbWVfbWluaW1hbCgpCiAgICAKYGBgCjwvZGl2PgoKVGhpcyBhcHByb2FjaCBpcyB1c2VmdWwsIGJlY2F1c2UgZmlyc3Qgb2ZmLCB0aGVzZSBzdHJhaWdodCBsaW5lcyBkb24ndCBsb29rICoqdG8qKiBjb3JyZWN0LCBhbmQgdGhleSBib2lsIHRoZSBjb21wbGV4IHNvY2lhbCBhbmQgbWF0aGVtYXRpY2FsIHJlbGF0aW9uc2hpcHMgaW52b2x2ZWQgaW4gdGhlIGFjdHVhbCBkYXRhIGRvd24gdG8ganVzdCB0d28gbnVtYmVyczoKCjEuIFRoZSBpbnRlcmNlcHQgb2YgdGhlIGxpbmUgKHRoZSAkeSQgdmFsdWUgd2hlbiAkeD0wJCkuCjIuIFRoZSBzbG9wZSAodGhlIGRlZ3JlZSB0byB3aGljaCAkeSQgY2hhbmdlcyBmb3IgZXZlcnkgaW5jcmVhc2Ugb2YgMSBmb3IgJHgkKS4KClR3byBudW1iZXJzIGFyZSBlYXN5IHRvIGxvb2sgYXQsIGFuZCBlYXN5IHRvIHVuZGVyc3RhbmQuIEl0J3MgYWxzbyB0aGUgY2FzZSB0aGF0IGlmIHRoZSBkYXRhIHdhcyBqdXN0IGEgbGl0dGxlIGJpdCBkaWZmZXJlbnQsIHRoZXNlIG51bWJlcnMgd291bGRuJ3QgY2hhbmdlIHZlcnkgbXVjaC5bXjFdCgoKVGhlIGxpbmVhciBtb2RlbGxpbmcgYXBwcm9hY2ggaXMgYWxzbyB1YmlxdWl0b3VzLiBFdmVyeSBmYW5jeSBvciBzdGF0ZSBvZiB0aGUgYXJ0IGVsYWJvcmF0aW9uIG9uIGxpbmVhciBtb2RlbGxpbmcgaXMsIGF0IGl0cyBjb3JlLCBzdGlsbCB0cnlpbmcgdG8gZGVzY3JpYmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhYmxlcyBpbiB0ZXJtcyBvZiBpbnRlcmNlcHRzIGFuZCBzbG9wZXMsIGluY2x1ZGluZzoKCi0gbG9naXN0aWMgcmVncmVzc2lvbgotIHBvaXNzb24gcmVncmVzc2lvbgotIHplcm8gaW5mbGF0ZWQgcG9pc3NvbiByZWdyZXNzaW9uCi0gbWl4ZWQgZWZmZWN0cyBtb2RlbHMKCk1vcmUgb3IgbGVzcywgbXkgYWR2aWNlIHdvdWxkIGlmIHlvdSBjYW4gZ2V0IGF3YXkgd2l0aCB1c2luZyBhIGxpbmVhciByZWdyZXNzaW9uIG9yIHNvbWUgZmFuY2llciB2ZXJzaW9uIG9mIGl0LCBnbyBmb3IgaXQuCgojIyBTdHJhaWdodCBsaW5lcyBhcmUgdG9vIHNpbXBsZSBzb21ldGltZXMKClNvbWV0aW1lcyB0aGUga2luZCBvZiBkYXRhIHdlJ3JlIHdvcmtpbmcgd2l0aCBpcyBvYnZpb3VzbHkgbW9yZSBjb21wbGV4IHRoYW4gY291bGQgcmVhc29uYWJseSBiZSBtb2RlbGVkIHVzaW5nIHN0cmFpZ2h0IGxpbmVzLiBIZXJlJ3MgYSBwbG90IG9mIHRoZSBmb3JtYW50IHRyYWplY3Rvcnkgb2Ygb25lIHRva2VuIG9mIFtheV0gbW92aW5nIHRocm91Z2ggdGhlIEYxeEYyIHNwYWNlLCBhbmQgYmVsb3cgdGhhdCBpcyBqdXN0IHRoZSBGMSB0cmFqZWN0b3J5IG9mIFtheV0gb3ZlciB0aW1lLgoKPGRpdiBzdHlsZSA9ICJ3aWR0aDo3NSU7bWFyZ2luLXJpZ2h0OmF1dG87bWFyZ2luLWxlZnQ6YXV0bzsiPgpgYGB7ciBmaWcud2lkdGg9Mi41LCBmaWcuaGVpZ2h0ID0gMS41LCBlY2hvID0gRiwgb3V0LndpZHRoID0gIjEwMCUifQpteV9pdWEgPC0gcmVhZC5jc3YoImF1ZGlvL215X2l1YS5jc3YiKQpteV9heSA8LSByZWFkLmNzdigiYXVkaW8vbXlfYXkuVGFibGUiKQpheV9mMSA8LSBsb2VzcyhGMSB+IHRpbWUsIGRhdGEgPSBteV9heSkKYXlfZjIgPC0gbG9lc3MoRjIgfiB0aW1lLCBkYXRhID0gbXlfYXkpCgpwcmVkIDwtIG15X2F5CnByZWQkRjEgPC0gZml0dGVkKGF5X2YxKQpwcmVkJEYyIDwtIGZpdHRlZChheV9mMikKcHJlZCAlPiUKICBtdXRhdGUoRjFfdCA9IGxhZyhGMSksCiAgICAgICAgIEYyX3QgPSBsYWcoRjIpLAogICAgICAgICBGMV9kID0gbG9nKEYxKS1sb2coRjFfdCksCiAgICAgICAgIEYyX2QgPSBsb2coRjIpLWxvZyhGMl90KSwKICAgICAgICAgdmVsID0gc3FydCgoRjFfZF4yKSArIChGMl9kXjIpKSwKICAgICAgICAgdGltZTIgPSB0aW1lLW1lZGlhbih0aW1lKSklPiUKICBmaWx0ZXIoaXMuZmluaXRlKHZlbCkpLT5wcmVkCgoKZ2dwbG90KG15X2l1YSwgYWVzKEYyLCBGMSkpKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHZjbGFzcykpKwogICAgZ2VvbV9wYXRoKGRhdGEgPSBwcmVkLCBhZXMoY29sb3IgPSB0aW1lMikpKwogICAgZ2VvbV9wb2ludChkYXRhID0gcHJlZCwgYWVzKHNpemU9MS92ZWwsIGNvbG9yID0gdGltZTIpKSsKICAgIHNjYWxlX3lfcmV2ZXJzZSgpKwogICAgc2NhbGVfeF9yZXZlcnNlKCkrCiAgICBzY2FsZV9zaXplKGd1aWRlID0gIm5vbmUiKSsKICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50MigiY2VudGVyZWQgdGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiIzQ0NzdBQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gICIjRERDQzc3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiI0NDNjY3NyIpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKYGBge3IgZmlnLndpZHRoPTIuNSwgZmlnLmhlaWdodCA9IDEuNSwgZWNobyA9IEYsIG91dC53aWR0aCA9ICIxMDAlIn0KZ2dwbG90KHByZWQsIGFlcyh0aW1lMiwgRjEpKSsKICAgIGdlb21fcGF0aChkYXRhID0gcHJlZCwgYWVzKGNvbG9yID0gdGltZTIpKSsKICAgIGdlb21fcG9pbnQoZGF0YSA9IHByZWQsIGFlcyhzaXplPTEvdmVsLCBjb2xvciA9IHRpbWUyKSkrCiAgICBzY2FsZV9zaXplKGd1aWRlID0gIm5vbmUiKSsKICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50MigiY2VudGVyZWQgdGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiIzQ0NzdBQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gICIjRERDQzc3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiI0NDNjY3NyIpKwogICAgeGxhYigiY2VudGVyZWQgdGltZSIpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKCjwvZGl2PgoKV2UgKmNvdWxkKiB0cnkgdG8gbW9kZWwgdGhlc2UgdHJhamVjdG9yaWVzIHVzaW5nIHN0cmFpZ2h0IGxpbmVzLiBCdXQgaWYgd2UgZG8gdGhpcyBvbiBhIHN1YnNldCBvZiAvYXkvIGZvcm1hbnQgdHJhY2sgZGF0YSBmcm9tIGEgc3Vic2V0IG9mIHBlb3BsZSBpbiB0aGUgUE5DLCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbGluZXMgaW4gYmx1ZSBhcmUgbG9zaW5nIHNvbWUgaW1wb3J0YW50IGluZm9ybWF0aW9uIHRoYXQgd2UgY2FuIHNlZSBpcyBwcmVzZW50IGluIHRoZSBkYXRhLgoKYGBge3IgZWNobyA9IEZ9CmF5MF9kZiA8LSByZWFkUkRTKCJkYXRhL2F5MF9kZi5yZHMiKQpheTBfZGYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShnZW5kZXIgPSBjYXNlX3doZW4oc2V4ID09ICJmIiB+ICJ3b21lbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIm1lbiIpLAogICAgICAgICBpZHN0cmluZyA9IGZhY3RvcihpZHN0cmluZykpICU+JQogIGdyb3VwX2J5KGlkc3RyaW5nLCBpZCkgJT4lCiAgbXV0YXRlKHRfbWluID0gdCAtIG1pbih0KSwKICAgICAgICAgdF9wcm9wID0gdF9taW4vbWF4KHRfbWluKSwKICAgICAgICAgbiA9IDE6bigpKS0+YXkwX2RmCmBgYAoKCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CmBgYHtyIGZpZy53aWR0aD0yLjUsIGZpZy5oZWlnaHQgPSAxLjUsIGVjaG8gPSBGLCBvdXQud2lkdGggPSAiMTAwJSJ9CmF5MF9kZiAlPiUKICBmaWx0ZXIoZG9iICVpbiUgYygxOTY0LCAxOTY1KSklPiUKICBncm91cF9ieShpZHN0cmluZywgZG9iLCBnZW5kZXIsIG4pJT4lCiAgc3VtbWFyaXNlKEYxID0gbWVhbihGMSksCiAgICAgICAgICAgIHRfcHJvcCA9IG1lYW4odF9wcm9wKSkgLT4gYXlfdHJhamVjdG9yeQoKYXlfdHJhamVjdG9yeSAlPiUKICBnZ3Bsb3QoYWVzKHRfcHJvcCwgRjEpKSsKICAgIGdlb21fbGluZShhZXMoZ3JvdXAgPSBpZHN0cmluZyksIGNvbG9yID0gImdyZXk1MCIpKwogICAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIikrCiAgICBmYWNldF93cmFwKH5nZW5kZXIpICsgCiAgICB0aGVtZV9idygpKwogICAgZ2d0aXRsZSgiZG9iID0gMTk2NCwgMTk2NSwgbGluZWFyIG1vZGVsIikKYGBgCjwvZGl2PgoKV2hhdCB3ZSB3YW50IGlzIHNvbWUgd2F5IG9mIGNoYXJhY3Rlcml6aW5nIGFuIGFyYml0cmFyeSBjdXJ2eSBsaW5lIGxpa2UgdGhlIHNpbmdsZSBGMSBmb3JtYW50IHRyYWNrIGJlbG93LCB1c2luZyBqdXN0IGEgZmV3IG51bWJlcnMsIGluIGEgd2F5IHRoYXQgaXMgKHJlbGF0aXZlbHkpIGVhc3kgdG8gaW50ZXJwcmV0IGFuZCBleHRyYXBvbGF0ZSBmcm9tLgoKPGRpdiBzdHlsZSA9ICJ3aWR0aDo4NSU7bWFyZ2luLXJpZ2h0OmF1dG87bWFyZ2luLWxlZnQ6YXV0bzsiPgoKYGBge3IgZmlnLndpZHRoPTIuNSwgZmlnLmhlaWdodCA9IDEuNSwgZWNobyA9IEYsIG91dC53aWR0aCA9ICIxMDAlIn0KYXlfdHJhamVjdG9yeSAlPiUKICBncm91cF9ieShpZHN0cmluZyklPiUKICBtdXRhdGUoZmlyc3QgPSBGMVsxXSkgJT4lCiAgZmlsdGVyKGZpcnN0ID4gNzEwKSAtPmF5X3NpbmdsZQpheV9zaW5nbGUgJT4lCiAgZ2dwbG90KGFlcyh0X3Byb3AsIEYxKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBnZW9tX2xpbmUoKSsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKPC9kaXY+CgpPbmUgd2F5IHlvdSBtaWdodCBoYXZlIHNlZW4gb2YgZG9pbmcgdGhpcyBpcyB1dGlsaXppbmcgInBvbHlub21pYWwiIHJlZ3Jlc3Npb24uCgpgYGB7cn0KbW9kZWwgPC0gbG0oRjEgfiB0X3Byb3AgKyBJKHRfcHJvcF4yKSArIEkodF9wcm9wXjMpLCBkYXRhID0gYXlfc2luZ2xlKQpgYGAKCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KYGBge3IgZmlnLndpZHRoPTIuNSwgZmlnLmhlaWdodCA9IDEuNSwgZWNobyA9IEYsIG91dC53aWR0aCA9ICIxMDAlIn0KYXlfc2luZ2xlICU+JQogIG11dGF0ZShwcmVkID0gcHJlZGljdChtb2RlbCkpICU+JQogIGdncGxvdChhZXModF9wcm9wLCBGMSkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgZ2VvbV9saW5lKCkrCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkKSwgY29sb3IgPSAicmVkIikrCiAgICB0aGVtZV9taW5pbWFsKCkKYGBgCjwvZGl2PgoKVGhpcyBnZXRzIGNsb3NlciB0byBmaXR0aW5nIHRoZSBkYXRhLCBidXQgdGhlIGJpZyBwcm9ibGVtIGhlcmUgaXMgdW5kZXJzdGFuZGluZyB3aGF0IG9uIGVhcnRoICR0XjMkIG1lYW5zIQoKCiMgIkJhc2lzIiBmdW5jdGlvbnMKCk9uZSB3YXkgdG8gYXBwcm94aW1hdGUgYW4gYXJiaXRyYXJ5IGN1cnZlIHdpdGggYSBzbWFsbCBudW1iZXIgb2YgY29lZmZpY2llbnRzIGlzIHRvIHVzZSBhICJiYXNpcyIgZnVuY3Rpb24uClRoZSBiYXNpcyBmdW5jdGlvbiBpcyBhIGNvbGxlY3Rpb24gb2YgY3VydmVzIHRoYXQgeW91IGNhbiBzY2FsZSwgdGhlbiBzdW0gb3ZlciwgdG8gYXBwcm94aW1hdGUgdGhlIGN1cnZlIG9mIGludGVyZXN0LgpJZiB5b3UndmUgZXZlciB0YWtlbiBpbnRybyB0byBwaG9uZXRpY3MsIHlvdSdyZSBhY3R1YWxseSBhbHJlYWR5IGZhbWlsaWFyIHdpdGggb25lIGJhc2lzIGZ1bmN0aW9uLCBldmVuIGlmIHlvdSdyZSBub3QgYXdhcmUgb2YgaXQhCgoKIyMgVGhlIEZvdXJpZXIgVHJhbnNmb3JtIQoKV2hhdCBpcyB0aGUgY3VydmUgb2YgZ3JlYXRlc3QgaW50ZXJlc3QgdG8gbGluZ3Vpc3RzIGlmIG5vdCBhIHdhdmVmb3JtPyAKCmBgYHtyIGVjaG8gPSBGfQp3YXYgPC0gcmVhZFdhdmUoImF1ZGlvL3Nob3R3YXYud2F2IikKYGBgCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CmBgYHtyIGVjaG8gPSBGfQp0aWJibGUocHJlc3N1cmUgPSBjKDAsd2F2QGxlZnQsMCkpICU+JQogIG11dGF0ZShzYW1wID0gMTpuKCkpIC0+d2F2X2RmCndhdl9kZiAlPiUKICBnZ3Bsb3QoYWVzKHNhbXAsIHByZXNzdXJlKSkrCiAgICBnZW9tX2xpbmUoKSsKICAgIHRoZW1lX3ZvaWQoKQpgYGAKPC9kaXY+CgpUaGlzIHdhdmVmb3JtIGlzIGZyb20gYSB2ZXJ5IHNob3J0IGNsaXAgKDE2IG1pbGxpc2Vjb25kcykgb2YgbWUgc2F5aW5nIFtpXS4KSnVzdCB0aGlzIHZlcnkgc2hvcnQgY2xpcCBoYXMgNzQyIGRhdGEgcG9pbnRzIChvciBzYW1wbGVzKSBpbiBpdC4KQnV0IHdlIGNhbiBhcHByb3hpbWF0ZSB0aGUgd2F2ZWZvcm0gd2l0aCBhIG11Y2ggc21hbGxlciBudW1iZXIgb2YgdmFsdWVzIHVzaW5nIHRoZSBGb3VyaWVyIHRyYW5zZm9ybS4KCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KYGBge3IgZWNobyA9IEZ9CmZvdXJpZXIgPC0gY3JlYXRlLmZvdXJpZXIuYmFzaXMocmFuZ2V2YWwgPSBjKDEsbWF4KHdhdl9kZiRzYW1wKSksCiAgICAgICAgICAgICAgICAgICAgIG5iYXNpcyA9IDUpCmZfYmFzaXMgPC0gZXZhbC5iYXNpcygxOjc0MiwgZm91cmllcilbLC0xXQphcy5kYXRhLmZyYW1lKGZfYmFzaXMpICU+JQogIG11dGF0ZShzYW1wID0gMTpuKCkpIC0+Zm91cmllcl9kZgpmb3VyaWVyX2RmICU+JQogIGdhdGhlcihjdXJ2ZSwgdmFsdWUsIC1zYW1wKSAlPiUKICBtdXRhdGUobiA9IDE6bigpKS0+bG9uZwoKbG9uZyAlPiUKICBtdXRhdGUoY3VydmVuID0gYXMubnVtZXJpYyhmYWN0b3IoY3VydmUsIGxldmVscyA9IGNvbG5hbWVzKGZfYmFzaXMpKSkpIC0+bG9uZwoKbG9uZyAlPiUKICBnZ3Bsb3QoYWVzKHNhbXAsIHZhbHVlLCBjb2xvciA9IGN1cnZlKSkrCiAgICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gY3VydmUpLCBzaXplID0gMikrCiAgICBzY2FsZV9jb2xvcl9jb2xvcmJsaW5kKGd1aWRlID0gIm5vbmUiKSsKICAgIHRoZW1lX3ZvaWQoKSsKICAgIGdndGl0bGUoIkEgRm91cmllciBCYXNpcyEiKQpgYGAKCjwvZGl2PgoKVGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgdmVyeSBzbWFsbCBGb3VyaWVyIGJhc2lzLgpJdCdzIGEgY29sbGVjdGlvbiBvZiA0IHNpbmUgYW5kIGNvc2luZSBjdXJ2ZXMgYWNyb3NzIHRoZSBpbnRlcnZhbCBvZiBpbnRlcmVzdC4KT25lIHRoaW5nIHlvdSBjYW4gc2VlIGlzIHRoYXQgZWFjaCBuZXh0IGN1cnZlIGlzIG9zY2lsbGF0aW5nIGF0IGEgaGlnaGVyIGZyZXF1ZW5jeS4KT25lIG9mIHRoZW0gb25seSBoYXMgb25lIHBlYWsgaW4gdGhlIGludGVydmFsLCB3aGlsZSB0aGUgbmV4dCBoYXMgdHdvLCB0aGVuIHRocmVlLCBldGMuCgpUaGUgd2F5IHlvdSBhcHByb3hpbWF0ZSBhbiBhcmJpdHJhcnkgY3VydmUgd2l0aCB0aGlzIGNvbGxlY3Rpb24gb2YgY3VydmVzIGlzIGJ5IHNjYWxpbmcgZWFjaCBvbmUuClRoYXQgaXMsIHlvdSBtYWtlIGl0IGJpZ2dlciBvciBzbWFsbGVyIGJ5IG11bHRpcGx5aW5nIGl0IGJ5IHNvbWUgbnVtYmVyLgpUaGVuLCB5b3Ugc2NhbiBhY3Jvc3MgdGhlIHggYXhpcywgc3VtbWluZyB1cCB0aGUgdmFsdWVzIHRvIGdldCB0aGUgZXN0aW1hdGUgZm9yIHRoZSBjdXJ2ZS4KCgpgYGB7ciBlY2hvID0gRn0KY3VydmVfb3JkZXIgPC0gY29sbmFtZXMoZm91cmllcl9kZikKY3VydmUwIDwtIGxvbmcgJT4lIG11dGF0ZShzdGVwID0gMSkKY3VydmUxIDwtIGxvbmcgJT4lCiAgICAgICAgICAgIG11dGF0ZSh2YWx1ZSA9IGNhc2Vfd2hlbihjdXJ2ZSA9PSBjdXJ2ZV9vcmRlclsxXSB+IHZhbHVlKjAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB2YWx1ZSksCiAgICAgICAgICAgICAgICAgICBzdGVwID0gMikKY3VydmUyIDwtIGN1cnZlMSAlPiUKICAgICAgICAgICAgbXV0YXRlKHZhbHVlID0gY2FzZV93aGVuKGN1cnZlID09IGN1cnZlX29yZGVyWzJdICB+IHZhbHVlKjAuNzUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gdmFsdWUpLAogICAgICAgICAgICAgICAgICAgc3RlcCA9IDMpCmN1cnZlMyA8LSBjdXJ2ZTIgJT4lCiAgICAgICAgICAgIG11dGF0ZSh2YWx1ZSA9IGNhc2Vfd2hlbihjdXJ2ZSA9PSBjdXJ2ZV9vcmRlclszXSB+IHZhbHVlKjEuMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB2YWx1ZSksCiAgICAgICAgICAgICAgICAgICBzdGVwID0gNCkKY3VydmU0IDwtIGN1cnZlMyAlPiUKICAgICAgICAgICAgbXV0YXRlKHZhbHVlID0gY2FzZV93aGVuKGN1cnZlID09IGN1cnZlX29yZGVyWzRdIH4gdmFsdWUqMC4xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHZhbHVlKSwKICAgICAgICAgICAgICAgICAgIHN0ZXAgPSA1KQpjdXJ2ZTUgPC0gY3VydmU0ICU+JQogICAgICAgICAgICBtdXRhdGUodmFsdWUgPSBjYXNlX3doZW4oY3VydmUgPT0gY3VydmVfb3JkZXJbNV0gfiB2YWx1ZSowLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gdmFsdWUpLAogICAgICAgICAgICAgICAgICAgc3RlcCA9IDYpCmJpbmRfcm93cyhjdXJ2ZTAsIGN1cnZlMSwgY3VydmUyLCBjdXJ2ZTMsY3VydmU0LCBjdXJ2ZTUpICU+JQogIGdncGxvdChhZXMoc2FtcCwgdmFsdWUsIGNvbG9yID0gY3VydmUpKSsKICAgIGdlb21fbGluZShhZXMoZ3JvdXAgPSBjdXJ2ZSksIHNpemUgPSAyKSsKICAgIHNjYWxlX2NvbG9yX2NvbG9yYmxpbmQoZ3VpZGUgPSAibm9uZSIpKwogICAgdGhlbWVfdm9pZCgpKwogICAgZ2d0aXRsZSgiQSBGb3VyaWVyIEJhc2lzISIpKwogICAgdHJhbnNpdGlvbl9zdGF0ZXMoc3RlcCwKICAgICAgICAgICAgICAgICAgICB0cmFuc2l0aW9uX2xlbmd0aCA9IDIsCiAgICAgICAgICAgICAgICAgICAgc3RhdGVfbGVuZ3RoID0gMSktPmFuaW0xCmFuaW1fc2F2ZShmaWxlbmFtZSA9ICJmaWd1cmVzL3dlaWdodF9mb3VyaWVyLmdpZiIsIGFuaW1hdGlvbiA9IGFuaW0xKQogIApgYGAKCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KIVtdKGZpZ3VyZXMvd2VpZ2h0X2ZvdXJpZXIuZ2lmKQo8L2Rpdj4KCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CmBgYHtyIGVjaG8gPSBGfQpjdXJ2ZTUgJT4lCiAgZ3JvdXBfYnkoc2FtcCkgJT4lCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSktPmZpbmFsX2N1cnZlCgpjdXJ2ZTUgJT4lCmdncGxvdChhZXMoc2FtcCwgdmFsdWUsIGNvbG9yID0gY3VydmUpKSsKICAgIGdlb21fbGluZShhZXMoZ3JvdXAgPSBjdXJ2ZSksIHNpemUgPSAyKSsKICAgIGdlb21fbGluZShkYXRhID0gZmluYWxfY3VydmUsIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gMiwgc2l6ZSA9IDIpKwogICAgc2NhbGVfY29sb3JfY29sb3JibGluZChndWlkZSA9ICJub25lIikrCiAgICB0aGVtZV92b2lkKCkrCiAgICBnZ3RpdGxlKCJBIEZvdXJpZXIgQmFzaXMhIikKCmBgYAo8L2Rpdj4KCk5vdywgNCBjdXJ2ZXMgYXJlIG5vdCBlbm91Z2ggdG8gY2FwdHVyZSB0aGUgY29tcGxleGl0eSBvZiB0aGUgd2F2ZWZvcm0gYWJvdmUuCldpdGggYSBsYXJnZXIgbnVtYmVyIG9mIGN1cnZlcywgZWFjaCBvc2NpbGxhdGluZyBhdCBhIGZhc3RlciBhbmQgZmFzdGVyIHJhdGUsIHdlIGNhbiBnZXQgY2xvc2VyLgpGb3IgZXhhbXBsZSwgdGhlIGFwcHJveGltYXRpb24gd2UgZ2V0IHdpdGggYSBGb3VyaWVyIGJhc2lzIG9mIDE1IGN1cnZlcyBpcyBwcmV0dHkgY2xvc2UsIGJ1dCBzdGlsbCBvdmVyLXNtb290aGluZyBpdC4KCmBgYHtyIGVjaG8gPSBGfQpmb3VyaWVyIDwtIGNyZWF0ZS5mb3VyaWVyLmJhc2lzKHJhbmdldmFsID0gYygxLG1heCh3YXZfZGYkc2FtcCkpLAogICAgICAgICAgICAgICAgICAgICBuYmFzaXMgPSAxNSkKZl9iYXNpcyA8LSBldmFsLmJhc2lzKDE6NzQyLCBmb3VyaWVyKQphcy5kYXRhLmZyYW1lKGZfYmFzaXMpICU+JQogIG11dGF0ZShzYW1wID0gMTpuKCkpIC0+Zm91cmllcl9kZgoKCndhdl9kZiAlPiUKICBsZWZ0X2pvaW4oZm91cmllcl9kZikgJT4lCiAgc2VsZWN0KC1zYW1wLCAtY29uc3QpICU+JQogIGxtKHByZXNzdXJlIH4gLiwgZGF0YT0uKSAtPiBzbWFsbF9tb2RlbApgYGAKCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KYGBge3IgZWNobyA9IEZ9Cndhdl9kZiAlPiUKICBtdXRhdGUocHJlZCA9IHByZWRpY3Qoc21hbGxfbW9kZWwpKSU+JQogIGdncGxvdChhZXMoc2FtcCwgcHJlc3N1cmUpKSArCiAgICBnZW9tX2xpbmUoKSsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWQpLCBjb2xvciA9ICJyZWQiKSsKICAgIGdndGl0bGUoIm5iYXNpcyA9IDE1IikrCiAgICB0aGVtZV92b2lkKCkKYGBgCjwvZGl2PgoKSGVyZSdzIGFuIGFuaW1hdGlvbiBvZiBob3cgbXVjaCBjbG9zZXIgYW5kIGNsb3NlciBvdXIgbW9kZWwgY2FuIGFwcHJveGltYXRlIHRoZSB3YXZlZm9ybSBhcyB3ZSBpbmNyZWFzZSB0aGUgbnVtYmVyIG9mIGN1cnZlcyBpbiB0aGUgYmFzaXMuCgpgYGB7ciBlY2hvID0gRn0KbWFrZV9maXQyIDwtIGZ1bmN0aW9uKGF5X3NpbmdsZSwgbmJhc2lzKSB7CiAgZm91cmllcl9heSA8LSBjcmVhdGUuZm91cmllci5iYXNpcyhyYW5nZXZhbCA9IGMoMSxucm93KGF5X3NpbmdsZSkpLAogICAgICAgICAgICAgICAgICAgICAgIG5iYXNpcyA9IG5iYXNpcykKICBmX2Jhc2lzX2F5IDwtIGV2YWwuYmFzaXMoMTpucm93KGF5X3NpbmdsZSksIGZvdXJpZXJfYXkpCiAgYXMuZGF0YS5mcmFtZShmX2Jhc2lzX2F5KSAlPiUKICAgIG11dGF0ZShzYW1wID0gMTpuKCkpIC0+Zm91cmllcl9kZl9heQogIAogIGF5X3NpbmdsZSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIHNlbGVjdChwcmVzc3VyZSkgJT4lCiAgICBtdXRhdGUoc2FtcCA9IDE6bigpKSAlPiUKICAgIGxlZnRfam9pbihmb3VyaWVyX2RmX2F5KSAlPiUKICAgIHNlbGVjdCgtc2FtcCwgLWNvbnN0KSAlPiUKICAgIGxtKHByZXNzdXJlIH4gLiwgZGF0YT0uKSAtPiBheV9tb2RlbAogIAogIGF5X3NpbmdsZSAlPiUKICAgIG11dGF0ZShwcmVkID0gcHJlZGljdChheV9tb2RlbCksCiAgICAgICAgICAgbmJhc2lzID0gbmJhc2lzKSAtPm91dF9kZiAKICBvdXRfZGYgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBzZWxlY3Qoc2FtcCwgcHJlc3N1cmUsIHByZWQpJT4lCiAgICBnYXRoZXIoc291cmNlLCB2YWx1ZSwgYygicHJlc3N1cmUiLCAicHJlZCIpKS0+b3V0X2RmCiAgcmV0dXJuKG91dF9kZikKfQoKb2RkX3NlcmllczIgPC0gc2VxKDUsMTAxLCBieSA9IDIpCgphbGxfd2F2PC0gbWFwX2RmKG9kZF9zZXJpZXMyLCB+bWFrZV9maXQyKHdhdl9kZiwgLngpLCAuaWQgPSAibmJhc2lzIikKCmFsbF93YXYgJT4lCiAgbXV0YXRlKG5iYXNpcyA9IG9kZF9zZXJpZXMyW2FzLm51bWVyaWMobmJhc2lzKV0pLT5hbGxfd2F2CgphbGxfd2F2ICAlPiUKICAgIG11dGF0ZShzb3VyY2UgPSBmYWN0b3Ioc291cmNlLCBsZXZlbHMgPSBjKCJwcmVzc3VyZSIsICJwcmVkIikpKSAlPiUKICAgIGdncGxvdChhZXMoc2FtcCwgdmFsdWUsIGNvbG9yID0gc291cmNlKSkrCiAgICBnZW9tX2xpbmUoKSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJyZWQiKSkrCiAgICB0aGVtZV92b2lkKCkrCiAgICB0cmFuc2l0aW9uX3N0YXRlcyhuYmFzaXMpKwogICAgZWFzZV9hZXMoJ2N1YmljLWluLW91dCcpKwogICAgZ2d0aXRsZSgiRml0IHVzaW5nIG5iYXNpcyA9IHtjbG9zZXN0X3N0YXRlfSIpLT5hbmltMgphbmltX3NhdmUoZmlsZW5hbWUgPSAiZmlndXJlcy9mb3VyaWVyX2ZpdC5naWYiLCBhbmltMiwgCiAgICAgICAgICBmcHMgPSAxMCwgbmZyYW1lcyA9IDMwMCkKYGBgCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CiFbXShmaWd1cmVzL2ZvdXJpZXJfZml0LmdpZikKPC9kaXY+CgoKCgpgYGB7ciBlY2hvID0gRn0KZm91cmllcl9iaWcgPC0gY3JlYXRlLmZvdXJpZXIuYmFzaXMocmFuZ2V2YWwgPSBjKDEsbWF4KHdhdl9kZiRzYW1wKSksCiAgICAgICAgICAgICAgICAgICAgIG5iYXNpcyA9IDEwMSkKZl9iYXNpc19iaWcgPC0gZXZhbC5iYXNpcygxOjc0MiwgZm91cmllcl9iaWcpCmFzLmRhdGEuZnJhbWUoZl9iYXNpc19iaWcpICU+JQogIG11dGF0ZShzYW1wID0gMTpuKCkpIC0+Zm91cmllcl9kZl9iaWcKd2F2X2RmICU+JQogIGxlZnRfam9pbihmb3VyaWVyX2RmX2JpZykgJT4lCiAgc2VsZWN0KC1zYW1wLCAtY29uc3QpICU+JQogIGxtKHByZXNzdXJlIH4gLiwgZGF0YT0uKSAtPiBzaWxseV9tb2RlbApgYGAKCgoKCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KYGBge3IgZWNobyA9IEZ9Cndhdl9kZiAlPiUKICBtdXRhdGUocHJlZCA9IHByZWRpY3Qoc2lsbHlfbW9kZWwpKSU+JQogIGdncGxvdChhZXMoc2FtcCwgcHJlc3N1cmUpKSArCiAgICBnZW9tX2xpbmUoKSsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWQpLCBjb2xvciA9ICJyZWQiKSsKICAgIGdndGl0bGUoIm5iYXNpcyA9IDEwMSIpKwogICAgdGhlbWVfdm9pZCgpCmBgYAo8L2Rpdj4KClRoaXMgaWxsdXN0cmF0ZXMgYW4gaW1wb3J0YW50IHBvaW50IHRoYXQgd2UgbmVlZCB0byB0YWtlIHNlcmlvdXNseSB3aGVuIHdlJ3JlIGZpdHRpbmcgb3VyIG93biBtb2RlbHMgZm9yIHJlYWw6Cgo8ZGl2IGNsYXNzID0gImVtcGhhc2l6ZSI+QmlnZ2VyIEJhc2lzID0gTW9yZSBXaWdnbHk8L2Rpdj4KCgoKVGhlIG5leHQga2luZGEgZnVuIHRoaW5nIHRvIGxvb2sgYXQgaXMgdGhlIGFjdHVhbCAqd2VpZ2h0cyogdGhhdCB3ZSdyZSB1c2luZyB0byBzY2FsZSBlYWNoIGN1cnZlIGluIHRoZSBGb3VyaWVyIEJhc2lzLiBIZXJlJ3MgYSBsaXR0bGUgcGxvdCBvZiBhbGwgb2YgdGhlIGNvZWZmaWNpZW50cywgY29sb3IgY29kZWQgYmxhY2sgZm9yIHRoZSBiaWdnZXN0IHZhbHVlcyBhbmQgd2hpdGUgZm9yIHNtYWxsZXN0IHZhbHVlcywgc3RhY2tlZCBvbiB0b3Agb2YgZWFjaG90aGVyIGluIGEgc2luZ2xlIGNvbHVtbi4KCmBgYHtyLCBmaWcud2lkdGggPSAwLjIsIGZpZy5oZWlnaHQ9MSwgZWNobyA9IEZ9CnRpYmJsZShjb2VmID0gY29lZihzaWxseV9tb2RlbCkpICU+JQogIG11dGF0ZShuID0gMTpuKCksCiAgICAgICAgIHggPSAxKSAgJT4lCiAgZ2dwbG90KGFlcyh4LCBuKSkrCiAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBsb2coYWJzKGNvZWYpKSkpKwogICAgdGhlbWVfdm9pZCgpKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsYWNrIiwgZ3VpZGUgPSAibm9uZSIpCiAgCmBgYAoKCklmIHdlIGRpZCBhIGJpdCBtb3JlIG1hdGgsIGFuZCB0aGVuIGRpZCB0aGlzIHNhbWUgY3VydmUgZml0dGluZyBwcm9jZXNzIGluIGEgcm9sbGluZyB3aW5kb3cgYWNyb3NzIGFuIGVudGlyZSBhdWRpbyBmaWxlLCBhbmQgYXQgZWFjaCBuZXh0IHdpbmRvdyBwbGFjZWQgdGhlIGNvbHVtbiBvZiBjb2VmZmljaWVudHMgZm9yIGVhY2ggd2luZG93IG5leHQgdG8gdGhlIG9uZSBiZWZvcmUsIHRoZSBncmFwaCB3b3VsZCBzdGFydCB0byBsb29rIGxpa2Ugc29tZXRoaW5nIHByZXR0eSBmYW1pbGlhci4KCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KIVtdKGZpZ3VyZXMvaV9zcGVjdC5wbmcpCjwvZGl2PgoKCklmIHdlIGNvbWUgYmFjayB0byBvdXIgb3JpZ2luYWwgcXVlc3Rpb24gYWJvdXQgaG93IHRvIGFwcHJveGltYXRlIGEgY3VydmUgbWF0Y2hpbmcgdGhlIEYxIHRyYWplY3Rvcnkgb2YgW2F5XSBhYm92ZSwgd2UgY2FuIGFjdHVhbGx5IGFsc28gdXNlIGEgRm91cmllciBiYXNpcyEKVGhlIEZvdXJpZXIgdHJhbnNmb3JtIGNhbiBiZSB1c2VkIHRvIGRlc2NyaWJlZCBhbnkgY3VydmUuCgpgYGB7ciBlY2hvID0gRn0KCm9kZF9zZXJpZXMgPC0gc2VxKDEsMTcsIGJ5ID0gMikKCm1ha2VfZml0IDwtIGZ1bmN0aW9uKGF5X3NpbmdsZSwgbmJhc2lzKSB7CiAgZm91cmllcl9heSA8LSBjcmVhdGUuZm91cmllci5iYXNpcyhyYW5nZXZhbCA9IGMoMSxucm93KGF5X3NpbmdsZSkpLAogICAgICAgICAgICAgICAgICAgICAgIG5iYXNpcyA9IG5iYXNpcykKICBmX2Jhc2lzX2F5IDwtIGV2YWwuYmFzaXMoMTpucm93KGF5X3NpbmdsZSksIGZvdXJpZXJfYXkpCiAgYXMuZGF0YS5mcmFtZShmX2Jhc2lzX2F5KSAlPiUKICAgIG11dGF0ZShzYW1wID0gMTpuKCkpIC0+Zm91cmllcl9kZl9heQogIAogIGF5X3NpbmdsZSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIHNlbGVjdChGMSkgJT4lCiAgICBtdXRhdGUoc2FtcCA9IDE6bigpKSAlPiUKICAgIGxlZnRfam9pbihmb3VyaWVyX2RmX2F5KSAlPiUKICAgIHNlbGVjdCgtc2FtcCwgLWNvbnN0KSAlPiUKICAgIGxtKEYxIH4gLiwgZGF0YT0uKSAtPiBheV9tb2RlbAogIAogIGF5X3NpbmdsZSAlPiUKICAgIG11dGF0ZShwcmVkID0gcHJlZGljdChheV9tb2RlbCksCiAgICAgICAgICAgbmJhc2lzID0gbmJhc2lzKSAtPm91dF9kZiAKICBvdXRfZGYgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBzZWxlY3QodF9wcm9wLCBGMSwgcHJlZCklPiUKICAgIGdhdGhlcihzb3VyY2UsIHZhbHVlLCBjKCJGMSIsICJwcmVkIikpLT5vdXRfZGYKICByZXR1cm4ob3V0X2RmKQp9CgoKYWxsX2ZpdHMgPC0gbWFwX2RmKG9kZF9zZXJpZXMsIH5tYWtlX2ZpdChheV9zaW5nbGUsIC54KSwgLmlkID0gIm5iYXNpcyIpCmFsbF9maXRzICU+JQogIG11dGF0ZShuYmFzaXMgPSBvZGRfc2VyaWVzW2FzLm51bWVyaWMobmJhc2lzKV0pLT5hbGxfZml0cwoKYWxsX2ZpdHMgJT4lCiAgZ2dwbG90KGFlcyh0X3Byb3AsIHZhbHVlLCBjb2xvciA9IHNvdXJjZSwgc2l6ZT1zb3VyY2UsIGFscGhhID0gc291cmNlKSkrCiAgICBnZW9tX2xpbmUoKSsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJyZWQiKSkrCiAgICBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXMgPSBjKDEsMykpKwogICAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoMSwgMC40KSkrCiAgICB0aGVtZV9taW5pbWFsKCkrCiAgICB0cmFuc2l0aW9uX3N0YXRlcyhuYmFzaXMpKwogICAgZWFzZV9hZXMoJ2N1YmljLWluLW91dCcpKwogICAgZ2d0aXRsZSgiRml0IHVzaW5nIG5iYXNpcyA9IHtjbG9zZXN0X3N0YXRlfSIpLT5mb3JtYW50X2FuaW0KCiNhbmltX3NhdmUoZmlsZW5hbWUgPSAiZmlndXJlcy9mb3VyaWVyX2Zvcm1hbnQuZ2lmIiwgZm9ybWFudF9hbmltLCAKIyAgICAgICAgICBmcHMgPSAxMCwgbmZyYW1lcyA9IDEwMCkKYGBgCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CiFbXShmaWd1cmVzL2ZvdXJpZXJfZm9ybWFudC5naWYpCjwvZGl2PgoKKipIb3dldmVyKiosIHRoZSBGb3VyaWVyIGJhc2lzIGlzIHBhcnRpY3VsYXJseSBnb29kIGZvciB3YXZlZm9ybXMgYmVjdWFzZSB3ZSBrbm93IHRoYXQgdGhleSBjb25zaXN0IG9mIG9zY2lsbGF0aW5nIGN1cnZlcywgYW5kIHRoZSBmb3VyaWVyIGJhc2lzIGlzIG1hZGUgdXAgb2Ygb3NjaWxsYXRpbmcgY3VydmVzLiAKVGhpcyBtZWFucyB3ZSBjYW4gYWN0dWFsbHkgbG9vayBhdCB0aGUgY29lZmZpY2llbnRzIGZyb20gdGhlIEZvdXJpZXIgdHJhbnNmb3JtIGFuZCB0aGV5J2xsIGhhdmUgc29tZSBraW5kIG9mIG1lYW5pbmcgdG8gdXMuClRoaXMgaXNuJ3QgdHJ1ZSBmb3IgbW9zdCBjdXJ2ZXMgd2UncmUgaW50ZXJlc3RlZCBpbiwgdGhvdWdoLCBsaWtlIHRoZSBGMSBmb3JtYW50IHRyYWplY3RvcnkuCkluIHRoZWlyIDIwMDYgYm9vayBvbiBGdW5jdGlvbmFsIERhdGEgQW5hbHlzaXMsIFJhbXNheSAmIFNpbHZlcm1hbiBzYXkKCj4gQSBGb3VyaWVyIHNlcmllcyBpcyBsaWtlIG1hcmdhcmluZTogSXTigJlzIGNoZWFwIGFuZCB5b3UgY2FuIHNwcmVhZCBpdCBvbiBwcmFjdGljYWxseSBhbnl0aGluZywgYnV0IGRvbuKAmXQgZXhwZWN0IHRoYXQgdGhlIHJlc3VsdCB3aWxsIGJlIGV4Y2l0aW5nIGVhdGluZy4KCiMjIFNwbGluZXMsIGEuay5hLiBSb3NlbWFyeSBUcnVmZmxlIEFpb2xpCgpUaGVyZSBhcmUgbG90cyBvZiBkaWZmZXJlbnQga2luZHMgb2Ygc3BsaW5lcywgYnV0IHRoZXkgYWxsIGJhc2ljYWxseSBsb29rIGxpa2Ugc29tZSB2YXJpYW50IG9mIHRoaXM6Cgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CmBgYHtyIGZpZy53aWR0aD0yLjUsIGZpZy5oZWlnaHQgPSAxLjUsIGVjaG8gPSBGLCBvdXQud2lkdGggPSAiMTAwJSJ9CmRhdGEuZnJhbWUoYnMoeCA9IHNlcSgwLDEsbGVuZ3RoID0gMTAwKSwgZGYgPSA1LCBpbnRlcmNlcHQgPSBUKSkgJT4lCiAgbXV0YXRlKHRpbWUgPSBzZXEoMCwxLGxlbmd0aCA9IDEwMCkpJT4lCiAgZ2F0aGVyKHBvbHksCiAgICAgICAgIHZhbHVlLAogICAgICAgICAtdGltZSktPmJzX2Jhc2lzX2RmCgpic19iYXNpc19kZiAlPiUKICBnZ3Bsb3QoYWVzKHRpbWUsIHZhbHVlLCBjb2xvciA9IHBvbHkpKSsKICAgIGdlb21fbGluZShzaXplID0gMSkrCiAgICBzY2FsZV9jb2xvcl9jb2xvcmJsaW5kKGd1aWRlID0gIm5vbmUiKSsKICAgIGdndGl0bGUoIkEgYi1zcGxpbmUgQmFzaXMhIikrCiAgICB0aGVtZV9taW5pbWFsKCkKYGBgCjwvZGl2PgoKSnVzdCBsaWtlIHdlIHNhdyBiZWZvcmUsIHdlIGNhbiByZS1zY2FsZSBlYWNoIG9mIHRoZXNlIGN1cnZlcyB0aGVuIHN1bSBvdmVyIHRoZW0gdG8gZ2V0IHNvbWUgbmV3IGVzdGltYXRlZCBjdXJ2ZS4KCmBgYHtyIGVjaG89Rn0KYl9zcGxpbmVfY29lZnMgPC0gcnVuaWYoNSwgLTEsIDEpCmBgYAoKYGBge3IgZWNobyA9IEZ9CmN1cnZlX29yZGVyIDwtIHVuaXF1ZShic19iYXNpc19kZiRwb2x5KQpjdXJ2ZTAgPC0gYnNfYmFzaXNfZGYgJT4lIG11dGF0ZShzdGVwID0gMSkKY3VydmUxIDwtIGJzX2Jhc2lzX2RmICU+JQogICAgICAgICAgICBtdXRhdGUodmFsdWUgPSBjYXNlX3doZW4ocG9seSA9PSBjdXJ2ZV9vcmRlclsxXSB+IHZhbHVlICogYl9zcGxpbmVfY29lZnNbMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gdmFsdWUpLAogICAgICAgICAgICAgICAgICAgc3RlcCA9IDIpCmN1cnZlMiA8LSBjdXJ2ZTEgJT4lCiAgICAgICAgICAgIG11dGF0ZSh2YWx1ZSA9IGNhc2Vfd2hlbihwb2x5ID09IGN1cnZlX29yZGVyWzJdICB+IHZhbHVlKiBiX3NwbGluZV9jb2Vmc1syXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB2YWx1ZSksCiAgICAgICAgICAgICAgICAgICBzdGVwID0gMykKY3VydmUzIDwtIGN1cnZlMiAlPiUKICAgICAgICAgICAgbXV0YXRlKHZhbHVlID0gY2FzZV93aGVuKHBvbHkgPT0gY3VydmVfb3JkZXJbM10gfiB2YWx1ZSogYl9zcGxpbmVfY29lZnNbM10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gdmFsdWUpLAogICAgICAgICAgICAgICAgICAgc3RlcCA9IDQpCmN1cnZlNCA8LSBjdXJ2ZTMgJT4lCiAgICAgICAgICAgIG11dGF0ZSh2YWx1ZSA9IGNhc2Vfd2hlbihwb2x5ID09IGN1cnZlX29yZGVyWzRdIH4gdmFsdWUqIGJfc3BsaW5lX2NvZWZzWzRdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHZhbHVlKSwKICAgICAgICAgICAgICAgICAgIHN0ZXAgPSA1KQpjdXJ2ZTUgPC0gY3VydmU0ICU+JQogICAgICAgICAgICBtdXRhdGUodmFsdWUgPSBjYXNlX3doZW4ocG9seSA9PSBjdXJ2ZV9vcmRlcls1XSB+IHZhbHVlKiBiX3NwbGluZV9jb2Vmc1s1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB2YWx1ZSksCiAgICAgICAgICAgICAgICAgICBzdGVwID0gNikKYmluZF9yb3dzKGN1cnZlMCwgY3VydmUxLCBjdXJ2ZTIsIGN1cnZlMyxjdXJ2ZTQsIGN1cnZlNSkgJT4lCiAgZ2dwbG90KGFlcyh0aW1lLCB2YWx1ZSwgY29sb3IgPSBwb2x5KSkrCiAgICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gcG9seSksIHNpemUgPSAyKSsKICAgIHNjYWxlX2NvbG9yX2NvbG9yYmxpbmQoZ3VpZGUgPSAibm9uZSIpKwogICAgdGhlbWVfbWluaW1hbCgpKwogICAgZ2d0aXRsZSgiQSBiLXNwbGluZSBCYXNpcyEiKSsKICAgIHRyYW5zaXRpb25fc3RhdGVzKHN0ZXAsCiAgICAgICAgICAgICAgICAgICAgdHJhbnNpdGlvbl9sZW5ndGggPSAyLAogICAgICAgICAgICAgICAgICAgIHN0YXRlX2xlbmd0aCA9IDEpKwogICAgZWFzZV9hZXMoImN1YmljLWluLW91dCIpLT5hbmltX2JzCiNhbmltX3NhdmUoZmlsZW5hbWUgPSAiZmlndXJlcy93ZWlnaHRfYnMuZ2lmIiwgCiMgICAgICAgICAgYW5pbWF0aW9uID0gYW5pbV9icykKICAKYGBgCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CiFbXShmaWd1cmVzL3dlaWdodF9icy5naWYpCjwvZGl2PgoKCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KCmBgYHtyIGZpZy53aWR0aD0yLjUsIGZpZy5oZWlnaHQgPSAxLjUsIGVjaG8gPSBGLCBvdXQud2lkdGggPSAiMTAwJSJ9CmN1cnZlNSAlPiUKICBncm91cF9ieSh0aW1lKSU+JQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSkpLT5ic19jdXJ2ZQoKY3VydmU1ICU+JQogIGdncGxvdChhZXModGltZSwgdmFsdWUsIGNvbG9yID0gcG9seSkpKwogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IHBvbHkpLCBzaXplID0gMSkrCiAgICBnZW9tX2xpbmUoZGF0YSA9IGJzX2N1cnZlLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIsCiAgICAgICAgICAgICAgc2l6ZSA9IDEpKwogICAgc2NhbGVfY29sb3JfY29sb3JibGluZChndWlkZSA9ICJub25lIikrCiAgICB0aGVtZV9taW5pbWFsKCkrCiAgICBleHBhbmRfbGltaXRzKHkgPSAxKSsKICAgIGdndGl0bGUoIkEgYi1zcGxpbmUgQmFzaXMhIikKYGBgCjwvZGl2PgoKCkhlcmUncyBhIGxpdHRsZSBhbmltYXRpb24gc2hvd2luZyBhIHJhbmdlIG9mIGRpZmZlcmVudCBjdXJ2ZXMgd2UgY2FuIGdldCBvdXQgb2YgYSBiLXNwbGluZSBiYXNpcyB0aGlzIHNpemU6CgoKYGBge3IgZWNobyA9IEZ9CmJzX21hdCA8LSBicyh4ID0gc2VxKDAsMSxsZW5ndGggPSAxMDApLCBkZiA9IDUsIGludGVyY2VwdCA9IFQpCnJhbmRvbV9jb2VmcyA8LSBtYXRyaXgocnVuaWYoNSoyMCwgLTEsIDEpLCBuY29sID0gNSkKcmFuZG9tX2ZpdHMgPC0gcmFuZG9tX2NvZWZzICUqJSB0KGJzX21hdCkKZGF0YS5mcmFtZSh0KHJhbmRvbV9maXRzKSkgJT4lCiAgbXV0YXRlKHRpbWUgPSBzZXEoMCwxLGxlbmd0aCA9IDEwMCkpICU+JQogIGdhdGhlcihmaXQsIHZhbHVlLCAtdGltZSkgJT4lCiAgZ2dwbG90KGFlcyh0aW1lLCB2YWx1ZSkpKwogICAgZ2VvbV9saW5lKGNvbG9yID0gInJlZCIpKwogICAgdGhlbWVfbWluaW1hbCgpKwogICAgZ2d0aXRsZSgibG90cyBvZiBkaWZmZXJlbnQgYnNwbGluZSBmaXRzIikrCiAgICB0cmFuc2l0aW9uX3N0YXRlcyhmaXQsCiAgICAgICAgICAgICAgICAgICAgdHJhbnNpdGlvbl9sZW5ndGggPSAyLAogICAgICAgICAgICAgICAgICAgIHN0YXRlX2xlbmd0aCA9IDEpKwogICAgZWFzZV9hZXMoImN1YmljLWluLW91dCIpLT5hbmltX2JzX21hbnkKICAKI2FuaW1fc2F2ZShmaWxlbmFtZSA9ICJmaWd1cmVzL21hbnlfYnMuZ2lmIiwgCiMgICAgICAgICAgYW5pbWF0aW9uID0gYW5pbV9ic19tYW55KQpgYGAKCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KIVtdKGZpZ3VyZXMvbWFueV9icy5naWYpCjwvZGl2PgoKQW5kIGl0J3MgYWxzbyBpbXBvcnRhbnQgdG8gYWxzbyByZW1lbWJlcjoKCjxkaXYgY2xhc3MgPSAiZW1waGFzaXplIj5CaWdnZXIgQmFzaXMgPSBNb3JlIFdpZ2dseTwvZGl2PgoKYGBge3IgZWNobyA9IEZ9CnNwbGluZV9leGFtcGxlcyA8LSBydW5pZigyMCwgMCwgMSkKZGZfcmFuZ2UgPC0gNToyMAoKZml0X3NwbGluZSA8LSBmdW5jdGlvbih4LCBjb2Vmcyl7CiAgYmFzaXMgPC0gYnMoc2VxKDAsMSwgbGVuZ3RoID0gMTAwKSwgZGYgPSB4LCBpbnRlcmNlcHQgPSBUKQogIGZpdCA8LSAoY29lZnNbMTp4XSAlKiUgdChiYXNpcykpWzEsXQogIHRpYmJsZSh0aW1lID0gc2VxKDAsMSwgbGVuZ3RoID0gMTAwKSwKICAgICAgICAgZml0ID0gZml0LAogICAgICAgICBuc3BsaW5lID0geCktPm91dAogIHJldHVybihvdXQpCn0KCm1hcF9kZihkZl9yYW5nZSwgZml0X3NwbGluZSwgY29lZnMgPSBzcGxpbmVfZXhhbXBsZXMpICU+JQogIGdncGxvdChhZXModGltZSwgZml0KSkrCiAgICBnZW9tX2xpbmUoKSArCiAgICB0aGVtZV9taW5pbWFsKCkrCiAgICBnZ3RpdGxlKCJuYmFzaXMgPSB7Y2xvc2VzdF9zdGF0ZX0iKSsKICAgIHRyYW5zaXRpb25fc3RhdGVzKG5zcGxpbmUpKwogICAgZWFzZV9hZXMoImN1YmljLWluLW91dCIpIC0+IHdpZ2dseV9hbmltCgojYW5pbV9zYXZlKGZpbGVuYW1lID0gImZpZ3VyZXMvd2lnZ2x5X2JzLmdpZiIsIAojICAgICAgICAgIGFuaW1hdGlvbiA9IHdpZ2dseV9hbmltKQpgYGAKCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KIVtdKGZpZ3VyZXMvd2lnZ2x5X2JzLmdpZikKPC9kaXY+CgojIyMgS25vdHMKCk5vdyBpcyB0aGUgdGltZSB0byBpbnRyb2R1Y2UgYSBuZXcgbm90aW9uOiAqKmtub3RzKiouCllvdSdsbCBtb3N0IG9mdGVuIHNlZSBwZW9wbGUgdGFsa2luZyBhYm91dCBzcGxpbmVzIGluIHRlcm1zIG9mICJrbm90cyIgb3IgImNvbnRyb2wgcG9pbnRzIi4gCklmIHlvdSd2ZSBldmVycGxheWVkIGFyb3VuZCB3aXRoIG1ha2luZyB2ZWN0b3IgZ3JhcGhpY3MgaW4gc29tZXRoaW5nIGxpa2UgUGhvdG9zaG9wLCBJbGx1c3RyYXRvciwgb3IgSW5rc2NhcGUsIHlvdSdsbCBiZSBraW5kIG9mIGZhbWlsaWFyIHdpdGggd2l0aCB0aGUgbm90aW9uIG9mICJrbm90cyIuIApIZXJlJ3MgYSBmaWd1cmUgb2YgYSBsaW5lIEkgZHJldyBpbiBJbmtzY2FwZSB0aGF0IGhhcyBvbmUga25vdC4KCjxkaXYgc3R5bGUgPSAid2lkdGg6NjAlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KIVtdKGZpZ3VyZXMva25vdHMucG5nKQo8L2Rpdj4KClRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgbnVtYmVyIG9mICJrbm90cyIgYW5kIHRoZSBudW1iZXIgb2YgYmFzaXMgZnVuY3Rpb25zIGlzIGEgbGl0dGxlIGJpdCBjb21wbGljYXRlZFteMl0sIGJ1dCAqdXN1YWxseSogdGhlIG1vcmUga25vdHMsIHRoZSBtb3JlIGJhc2lzIHBvbHlub21pYWxzIHRoZXJlIGFyZSwgc28gd2UgY2FuIG5vdyBmdXJ0aGVyIHJldmlzZSBvdXIgc2F5aW5nIGFib3V0IHdpZ2dsaW5lc3MuCgo8ZGl2IGNsYXNzID0gImVtcGhhc2l6ZSI+TW9yZSBLbm90cyA9IE1vcmUgV2lnZ2x5PC9kaXY+CgpUaGUgYmFzaXMgd2Ugd2VyZSBsb29raW5nIGF0IGFib3ZlIGFjdHVhbGx5IGhhZCBvbmx5IG9uZSBrbm90LCBidXQgZXZlbiB3aXRoIHRoYXQgd2UgY2FuIGZpdCB0aGUgY3VydmUgb2YgdGhlIHRoYXQgc2luZ2xlIGZvcm1hbnQgdHJhY2sgcmVhbGx5IHdlbGwuCgpgYGB7ciBlY2hvID0gRn0KYnNfZGYgPC0gZGF0YS5mcmFtZShicyh4ID0gMTpucm93KGF5X3NpbmdsZSksIGRmID0gNSwgaW50ZXJjZXB0ID0gVCkpICU+JQogICAgICAgICAgbXV0YXRlKHRfcHJvcCA9IGF5X3NpbmdsZSR0X3Byb3ApCgoKYXlfc2luZ2xlICU+JQogIHVuZ3JvdXAoKSU+JQogIHNlbGVjdCh0X3Byb3AsIEYxKSU+JQogIGxlZnRfam9pbihic19kZiklPiUKICBzZWxlY3QoLXRfcHJvcCklPiUKICBsbShGMSB+IC4tMSwgZGF0YSAgPSAuKSAtPiBic19tb2QKYGBgCgoKCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CmBgYHtyIGZpZy53aWR0aD0yLjUsIGZpZy5oZWlnaHQgPSAxLjUsIGVjaG8gPSBGLCBvdXQud2lkdGggPSAiMTAwJSIsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nPSBGfQpheV9zaW5nbGUgJT4lCiAgdW5ncm91cCgpJT4lCiAgbXV0YXRlKHByZWQgPSBwcmVkaWN0KGJzX21vZCkpJT4lCiAgc2VsZWN0KHRfcHJvcCwgRjEsIHByZWQpJT4lCiAgZ2F0aGVyKHNvdXJjZSwgdmFsdWUsIC10X3Byb3ApICU+JQogIGdncGxvdChhZXModF9wcm9wLCB2YWx1ZSwgY29sb3IgPSBzb3VyY2UsIHNpemU9c291cmNlLCBhbHBoYSA9IHNvdXJjZSkpKwogICAgZ2VvbV9saW5lKCkrCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCAicmVkIikpKwogICAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYygxLDMpKSsKICAgIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBjKDEsIDAuNCkpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAo8L2Rpdj4KCiMjIyBJbnRlcnByZXRhdGlvbi4uLiBub3Qgc28gc3RyYWlnaHRmb3J3YXJkCgpUaGUgZG93bnNpZGUgdG8gc3BsaW5lcyBpcyB0aGF0IHRoZSBjb2VmZmljaWVudHMgd2UgZ2V0IGZvciBlYWNoIHBvbHlub21pYWwgYXJlbid0ICpyZWFsbHkqIGludGVycHJldGFibGUuIEhlcmUgYXJlIHRoZSA1IGNvZWZmaWNpZW50cyB0aGF0IGFyZSBiZWluZyB1c2VkIHRvIGZpdCB0aGUgY3VydmUgdG8gdGhlIGZvcm1hbnQgdHJhamVjdG9yeS4KCmBgYHtyIGVjaG8gPSBmfQpjb2VmKGJzX21vZCkKYGBgCgpUaGVzZSBkb24ndCByZWFsbHkgKm1lYW4qIGFueXRoaW5nIGxpa2UgdGhlIGZvdXJpZXIgdHJhbnNmb3JtIGNvZWZmaWNpZW50cyBkaWQuCkludGVycHJldGluZyB0aGUgcmVzdWx0cyBvZiBmaXR0aW5nIGEgbW9kZWwgd2l0aCBzcGxpbmVzIHdpbGwgdGFrZSBhIGxpdHRsZSBiaXQgbW9yZSB3b3JrLgpGb3IgdGhlIG1vc3QgcGFydCwgaXQgd2lsbCBpbnZvbHZlIG1ha2luZyB2aXN1YWxpemF0aW9ucyBvZiB0aGUgZGF0YS5haAoKCgoKCgoKCiMgVGhlIE1hdGggQmFzaWNzCgpIZXJlIGFyZSB0d28gcXVlc3Rpb25zIHRoYXQgYXJlIHByZXR0eSByZWFzaW9uYWJsZSByaWdodCBub3c6CgoxLiBPaywgaG93IGRvIGkgKmdldCogdGhlc2UgY3VydmVzPwoyLiBJc24ndCBtdWx0aXBseWluZyBhbmQgYWRkaW5nIGFsbCB0aGVzZSBjdXJ2ZXMgdG9nZXRoZXIgYSBsb3Qgb2Ygd29yaz8KClRoZSBhbnN3ZXJzIGFyZSAidGhlIGNvbXB1dGVyIGdpdmVzIHRoZW0gdG8gdXMiIGFuZCAic3VycHJpc2luZ2x5IG5vISIKCkZvciB0aGUgbW9zdCBwYXJ0LCB3ZSdyZSBub3QgZ29pbmcgdG8gYmUgY29uc3RydWN0aW5nIHNwbGluZSBiYXNlcyAiYnkgaGFuZCIgKHNlZSB0aGUgIkRvaW5nIGl0IGluIFIiIHNlY3Rpb24gYmVsb3cpLgpCdXQgZXZlbiB0aGVuIGl0J3Mgbm90IHRoYXQgdHJpY2t5LiAKVGhlIGBzcGxpbmVzYCBwYWNrYWdlIHdpbGwgbGV0IHVzIGNvbnN0cnVjdCBhIGItc3BsaW5lIGJhc2lzIGFzIGVhc2lsbHkgYXMgdGhpczoKCmBgYHtyfQpsaWJyYXJ5KHNwbGluZXMpCmJzcGxpbmVfYmFzaXMgPC0gYnMoMToxMCwgZGYgPSA1LCBpbnRlcmNlcHQgPSBUKQpic3BsaW5lX2Jhc2lzCmBgYAoKPGRpdiBzdHlsZSA9ICJ3aWR0aDo4NSU7bWFyZ2luLXJpZ2h0OmF1dG87bWFyZ2luLWxlZnQ6YXV0bzsiPgpgYGB7ciBmaWcud2lkdGg9Mi41LCBmaWcuaGVpZ2h0ID0gMS41LCBlY2hvID0gRiwgb3V0LndpZHRoID0gIjEwMCUiLCBtZXNzYWdlID0gRiwgd2FybmluZz0gRn0KbWF0cGxvdCgxOjEwLCBic3BsaW5lX2Jhc2lzLCB0eXBlID0gJ2wnKQpgYGAKPC9kaXY+CgpOb3cgbGV0cyBzYXkgd2UgY2hvb3NlIHRoZSBmb2xsb3dpbmcgNSBjb2VmZmljaWVudHMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQpjb2VmZmljaWVudHMgPC0gYygzLCA2LCAyLCA1LCAxKQpgYGAKClNjYWxpbmcgZWFjaCBwb2x5bm9taWFsIGFuZCBzdW1taW5nIHVwIGFjcm9zcyB0aGVtIGlzIGFzIGVhc3kgYXM6CgpgYGB7cn0KY29lZmZpY2llbnRzICUqJSB0KGJzcGxpbmVfYmFzaXMpCmBgYAoKVGhlIGAlKiVgIHN5bnRheCBpcyBkb2luZyBtYXRyaXggbXVsdGlwbGljYXRpb24uClVzdWFsbHkgeW91IGNhbiBnZXQgYXdheSB3aXRob3V0IHRoaW5raW5nIGFib3V0IG1hdHJpeCBtdWx0aXBsaWNhdGlvIG9yIGxpbmVhciBhbGdlYnJhIHdoZW4gZG9pbmcgc3RhdHMsIGJ1dCBzaW5jZSBpdCB1bmRlcmxpZXMgc28gbWFueSBxdWFudGl0YXRpdmUgYW5kIGNvbXB1dGF0aW9uYWwgbWV0aG9kcywgaXQncyBoYW5keSB0byBrbm93IGEgbGl0dGxlIGJpdCBhYm91dCBpdC4KClRoaXMgYW5pbWF0aW9uIGlsbHVzdHJhdGVzIHRoZSBtYXRoZW1hdGljYWwgb3BlcmF0aW9ucyB0aGF0IGFyZSBnb2luZyBvbiB3aGVuIHlvdSBkbyB0aGUgbWF0cml4IG11bHRpcGxpY2F0aW9uIHdyaXR0ZW4gb3V0IGFib3ZlLgoKIVtdKGZpZ3VyZXMvZm9vLmdpZikKCgojIERvaW5nIGl0IGluIFIKClRvIGZpdCBhIHNwbGluZSB0byBkYXRhIGluIFIsIHlvdSdsbCB3YW50IHRvIHVzZSB0aGUgYGdhbSgpYCBmdW5jdGlvbiwgd2hpY2ggc3RhbmRzIGZvciAiZ2VuZXJhbGl6ZWQgYWRkaXRpdmUgbW9kZWwiIGZyb20gdGhlIGBtZ2N2YCBwYWNrYWdlLiAKTGV0J3MgcmVtaW5kIG91cnNlbHZlcyBvZiB0aGUgW2F5XSB0cmFqZWN0b3J5IGRhdGEuCgpgYGB7ciBlY2hvID0gRn0KYXlfdHJhamVjdG9yeSA8LSBheV90cmFqZWN0b3J5ICU+JQogIHVuZ3JvdXAoKSU+JQogIG11dGF0ZShnZW5kZXIgPSBmYWN0b3IoZ2VuZGVyKSkKYGBgCgoKPGRpdiBzdHlsZSA9ICJ3aWR0aDo4NSU7bWFyZ2luLXJpZ2h0OmF1dG87bWFyZ2luLWxlZnQ6YXV0bzsiPgpgYGB7ciBmaWcud2lkdGg9Mi41LCBmaWcuaGVpZ2h0ID0gMS41LCBlY2hvID0gRiwgb3V0LndpZHRoID0gIjEwMCUifQpheTBfZGYgJT4lCiAgZmlsdGVyKGRvYiAlaW4lIGMoMTk2NCwgMTk2NSkpJT4lCiAgZ3JvdXBfYnkoaWRzdHJpbmcsIGRvYiwgZ2VuZGVyLCBuKSU+JQogIHN1bW1hcmlzZShGMSA9IG1lYW4oRjEpLAogICAgICAgICAgICB0X3Byb3AgPSBtZWFuKHRfcHJvcCkpIC0+IGF5X3RyYWplY3RvcnkKCmF5X3RyYWplY3RvcnkgJT4lCiAgZ2dwbG90KGFlcyh0X3Byb3AsIEYxKSkrCiAgICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gaWRzdHJpbmcpKSsKICAgICNzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKSsKICAgIGZhY2V0X3dyYXAofmdlbmRlcikgKyAKICAgIHRoZW1lX2J3KCkrCiAgICBnZ3RpdGxlKCJkb2IgPSAxOTY0LCAxOTY1LCBsaW5lYXIgbW9kZWwiKQpgYGAKPC9kaXY+CgpMZXQncyBmaXQgYSBnZW5lcmFsaXplZCBhZGRpdGl2ZSBtb2RlbCB0byB0aGlzIGRhdGEsIHdpdGggdGhlIGZvbGxvd2luZyBzcGVjaWZpY2F0aW9ucy4KCi0gV2Ugd2FudCB0byBlbnRlciBnZW5kZXIgaW50byB0aGUgbW9kZWwgbGlrZSB3ZSB3b3VsZCBmb3IgYW55IGxpbmVhciBtb2RlbC4KLSBXZSB3YW50IHRvIGZpdCBhIHNwbGluZSBvdmVyIGB0X3Byb3BgIHdpdGgsIGxldCdzIHNheSwgMTAga25vdHMuCi0gV2Ugd2FudCB0byBmaXQgc2VwYXJhdGUgc3BsaW5lcyBmb3IgZWFjaCBnZW5kZXIuCgpIZXJlJ3MgdGhlIGNvZGU6CgpgYGB7cn0KbGlicmFyeShtZ2N2KQpheV9tb2RlbCA8LSBnYW0oRjEgfiBnZW5kZXIgKyBzKHRfcHJvcCwgYnkgPSBnZW5kZXIsIGsgPSAxMCksIAogICAgICAgICAgICAgICAgZGF0YSA9IGF5X3RyYWplY3RvcnkpCmBgYAoKTm93LCBgZ2FtYCBpcyBkb2luZyBhIGxvdCBtb3JlIGZhbmN5IHN0dWZmIHRoYW4ganVzdCBmaW5kaW5nIHRoZSBiZXN0IGNvZWZmaWNpZW50cyBmb3IgZWFjaCBiYXNpcyBwb2x5bm9taWFsLiAKRm9yIG9uZSwgaXQncyBhdXRvbWF0aWNhbGx5IGlkZW50aWZ5aW5nIGFuZCB1dGlsaXppbmcgYSAic21vb3RoaW5nIHBhcmFtZXRlci4iCkFzIHRoZSBzbW9vdGhpbmcgcGFyYW1ldGVyIGluY3JlYXNlcywgaXQnbGwgcGVuYWxpemUgdGhlIGN1cnZlIGRvd24gdG8gYSBzdHJhaWdodCBsaW5lLgpXaGF0J3MgbmljZSBhYm91dCB0aGlzIGlzIHRoYXQgdGhlIHJlc3VsdGluZyBzbW9vdGggd29uJ3QgKHVzdWFsbHkpIGJlIHVuZHVlbHkgd2lnZ2x5LCBldmVuIGlmIHlvdSBoYXZlIG1hbnkgbWFueSBrbm90cy4KCklmIHdlIGxvb2sgYXQgYSBzdW1tYXJ5IG9mIGBheV9tb2RlbGAsIHRoZXJlJ3Mgc29tZSB1c2VmdWwgaW5mb3JtYXRpb24sIGJ1dCBpdCdzIGxlc3MgaW50ZXJwcmV0YWJsZSB0aGFuIGEgbm9ybWFsIGxpbmVhciByZWdyZXNzaW9uLgoKYGBge3J9CnN1bW1hcnkoYXlfbW9kZWwpCmBgYAoKVGhlIHNpZ25pZmljYW50IGdlbmRlciBlZmZlY3QgdGVsbHMgdXMgdGhhdCwgdW5zdXJwcmlzaW5nbHksIHdvbWVuIGhhZCBhIGhpZ2hlciBGMSB0aGFuIG1lbiBieSBhYm91dCAxMjcgSHouClRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlIHNtb290aCB0ZXJtcyBpc24ndCBhbGwgdGhhdCBtZWFuaW5nZnVsOiBpdCBtZWFucyB0aGUgc21vb3RocyB3ZXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IHRoYW4gYSBzdHJhaWdodCBmbGF0IGxpbmUuClRoZSBjb2x1bW4gY2FsbGVkIGBlZGZgIChzdGFuZHMgZm9yIEVzdGltYXRlZCBEZWdyZWVzIG9mIEZyZWVkb20pIGlzIHVzZWZ1bCB0byBleWViYWxsLgpJZiB0aGUgc21vb3RoIHdhcyBwZW5hbGl6ZWQgZG93biB0byBhIHN0cmFpZ2h0IGxpbmUsIGl0IHdvdWxkIGhhdmUgYW4gYW4gRURGIG9mIGFib3V0IDEuClRoZXNlIEVERnMgYXJlIGxhcmdlciB0aGFuIDEsIGFuZCB3b21lbidzIGFyZSBsYXJnZXIgdGhhbiBtZW4ncyAoYWx0aG91Z2gsIHdlJ2QgbmVlZCB0byBkbyBhIGxvdCBtb3JlIHdvcmsgdG8gc2VlIGlmIGl0IHdhcyAqc2lnbmlmaWNhbnRseSogbGFyZ2VyKSwgc28gaXQgbG9va3MgbGlrZSBpdCB3YXMgd29ydGggZml0dGluZyBhIGZhbmljZXIgbW9kZWwgdGhhbiBqdXN0IGEgbGluZWFyIG1vZGVsIQoKYGBge3IgZWNobyA9IEZ9CmxpYnJhcnkoaXRzYWR1ZykKYGBgCmBgYHtyIGVjaG8gPSBGfQpheV9wcmVkIDwtIGdldF9wcmVkaWN0aW9ucyhheV9tb2RlbCwgY29uZCA9IGxpc3QodF9wcm9wID0gc2VxKDAsMSxsZW5ndGggPSAxNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZGVyID0gYygibWVuIiwgIndvbWVuIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwcmludC5zdW1tYXJ5ID0gRikKYGBgCgpUbyByZWFsbHkgZXhwbG9yZSBnYW0gZml0cywgd2UgaGF2ZSB0byBwbG90IHRoZW0uIApJJ20gZ29pbmcgdG8gc3BhcmUgeW91IHRoZSBjb2RlIGZvciBtYWtpbmcgdGhlc2UgcGxvdHMgcmlnaHQgbm93LCBidXQgeW91IGNhbiBzY3J1dGluaXplIGl0IGJ5IGRvd25sb2FkaW5nIHRoZSBjb2RlIGZyb20gdGhlIGxpdHRsZSBkcm9wIGRvd24gYnV0dG9uIGF0IHRoZSB0b3Agb2YgdGhlIHBhZ2UuCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CmBgYHtyIGZpZy53aWR0aD0yLjUsIGZpZy5oZWlnaHQgPSAxLjUsIGVjaG8gPSBGLCBvdXQud2lkdGggPSAiMTAwJSJ9CmF5X3ByZWQgJT4lCiAgZ2dwbG90KGFlcyh0X3Byb3AsIGZpdCkpKwogICAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBmaXQtKDEuOTYgKiBDSSksIAogICAgICAgICAgICAgICAgICAgIHltYXggPSBmaXQgKyAoMS45NiAqIENJKSwgCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGdlbmRlciksCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNCkrCiAgICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gZ2VuZGVyKSkrCiAgICBzY2FsZV9jb2xvcl9jb2xvcmJsaW5kKCkrCiAgICBzY2FsZV9maWxsX2NvbG9yYmxpbmQoKSsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKPC9kaXY+CgpCdXQgaGVyZSdzIGFuIGltcG9ydGFudCB3YXJuaW5nIQoKPGRpdiBjbGFzcyA9ICJlbXBoYXNpemUiPkRvbid0IGp1c3QgbG9vayB0byBzZWUgaWYgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG9mIHRoZSBzbW9vdGhzIGFyZSBvdmVybGFwcGluZyBvciBub3QhPC9kaXY+CgpZb3UgY2FuJ3QgcmVhbGx5IHJlYWxseSB0ZWxsIGlmIHR3byBjdXJ2ZXMgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20ganVzdCBsb29raW5nIGF0IHdoZXRoZXIgdGhlaXIgY29uZmlkZW5jZSBpbnRlcnZhbHMgb3ZlcmxhcCwgY2F1c2UgdGhhdCdzIG5vdCByZWFsbHkgaG93IGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHdvcmsuCkluc3RlYWQsIHdlIG5lZWQgdG8gbG9vayBhdCBhIGRpZmZlcmVuY2UgY3VydmUsIHdoaWNoIHdlICpjYW4qIGdldCBmcm9tIGEgZ2FtIG1vZGVsLgoKPGRpdiBzdHlsZSA9ICJ3aWR0aDo4NSU7bWFyZ2luLXJpZ2h0OmF1dG87bWFyZ2luLWxlZnQ6YXV0bzsiPgpgYGB7ciBmaWcud2lkdGg9Mi41LCBmaWcuaGVpZ2h0ID0gMS41LCBlY2hvID0gRiwgb3V0LndpZHRoID0gIjEwMCUifQpnZXRfZGlmZmVyZW5jZShheV9tb2RlbCwgCiAgICAgICAgICAgICAgIGNvbXAgPSBsaXN0KGdlbmRlciA9IGMoIndvbWVuIiwgIm1lbiIpKSwKICAgICAgICAgICAgICAgY29uZCA9IGxpc3QodF9wcm9wID0gc2VxKDAsMSwgbGVuZ3RoID0gMTUpKSwKICAgICAgICAgICAgICAgcHJpbnQuc3VtbWFyeSA9IEYpIC0+IGRpZmZlcmVuY2VfY3VydmUKCmRpZmZlcmVuY2VfY3VydmUgJT4lCiAgZ2dwbG90KGFlcyh0X3Byb3AsIGRpZmZlcmVuY2UpKSsKICAgIGdlb21fcmliYm9uKGFlcyh5bWluID0gZGlmZmVyZW5jZS0oMS45NiAqIENJKSwgCiAgICAgICAgICAgICAgICAgICAgeW1heCA9IGRpZmZlcmVuY2UgKyAoMS45NiAqIENJKSksCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNCkrCiAgICBnZW9tX2xpbmUoKSsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMikrCiAgICB0aGVtZV9taW5pbWFsKCkrCiAgICBnZ3RpdGxlKCJFc3RpbWF0ZWQgd29tZW4gLSBtZW4gZGlmZmVyZW5jZSIpCmBgYAoKPC9kaXY+CgoKIyBSZWNhcCwgYW5kIEZ1cnRoZXIgSXNzdWVzCgojIyBSZWNhcAoKMS4gU3BsaW5lcyBhcmUgZml0IHVzaW5nIGEgImJhc2lzIiBvZiBwb2x5bm9taWFsIGN1cnZlcyB3aGljaCBhcmUgc2NhbGVkIGFuZCBzdW1tZWQgdG8gYXBwcm94aW1hdGUgYSBjdXJ2ZS4KMi4gT25lIG9mIHRoZSBzdHJlbmd0aHMgb2YgdXNpbmcgbm9uLWxpbmVhciBtZXRob2RzIGlzIHRoYXQgeW91IGNhbiBtb2RlbCByZWxhdGlvbnNoaXBzIHRoYXQgYXJlIGNsZWFybHkgbm9uIGxpbmVhci4gU29tZSB3ZWFrbmVzc2VzIGlzIHRoYXQgaXQgaXMgbW9yZSBjaGFsbGVuZ2luZyB0byBpbnRlcHJldCB0aGUgcmVzdWx0cywgYW5kIHdlIG5lZWQgdG8gYmUgY2FyZWZ1bCBvZiBvdmVyLWZpdHRpbmcuCjMuIFlvdSBjYW4gZml0IGEgc3BsaW5lIHVzaW5nIGdlbmVyYWxpemVkIGFkZGl0aXZlIG1vZGVscywgc3BlY2lmaWNhbGx5IGFzIGltcGxlbWVudGVkIGluIHRoZSBgbWdjdmAgcGFja2FnZS4KCiMjIFRoaW5ncyB5b3UnbGwgd2FudCBnYW1zIHRvIGRvLCBhbmQgdGhleSBjYW4KCiMjIyBSYW5kb20gSW50ZXJjZXB0cwoKRWFjaCBzcGVha2VyIGhhcyB0aGVpciBvd24gcGVyc29uYWwgYXZlcmFnZSBGMSB2YWx1ZSBmb3IgdGhlaXIgW2F5XSB0aGF0IHdvbid0IGJlIHBlcmZlY3RseSBjYXB0dXJlZCBieSB0aGVpciBkZW1vZ3JhcGhpYyBmZWF0dXJlcy4gCkZvciB0aGlzLCB5b3UnbGwgd2FudCB0byBmaXQgcmFuZG9tIGludGVyY2VwdHMgYnkgc3BlYWtlciwgKmFuZCB5b3UgY2FuKi4KCiMjIyBSYW5kb20gKnNtb290aHMqCgpFYWNoIHNwZWFrZXIgd2lsbCBhbHNvIGhhdmUgdGhlaXIgb3duIHBlcnNvbmFsIEYxICp0cmFqZWN0b3JpZXMqLCBhbmQgdG8gY2FwdHVyZSB0aGlzLCB5b3UnbGwgd2FudCB0byBmaXQgcmFuZG9tIHNtb290aHMgKGxpa2UgcmFuZG9tIHNsb3BlcyksICphbmQgeW91IGNhbiouCgojIyMgQXV0b2NvcnJlbGF0aW9uCgpJbXBvcnRhbnRseSwgdGhlIGRhdGEgd2UncmUgbW9kZWxsaW5nIGFyZW4ndCBqdXN0IGEgcmFuZG9tIGFzc29jaWF0aW9uIG9mIGRhdGEgcG9pbnRzIGJldHdlZW4gdGltZSBzdGFtcHMgYW5kIEYxIHZhbHVlcywgbGlrZSB0aGlzIHBsb3Qgc2hvd3MuCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KYGBge3IgZmlnLndpZHRoPTIuNSwgZmlnLmhlaWdodCA9IDEuNSwgZWNobyA9IEYsIG91dC53aWR0aCA9ICIxMDAlIn0KYXkwX2RmICU+JQogIGZpbHRlcihkb2IgJWluJSBjKDE5NjQsIDE5NjUpKSU+JQogIGdyb3VwX2J5KGlkc3RyaW5nLCBkb2IsIGdlbmRlciwgbiklPiUKICBzdW1tYXJpc2UoRjEgPSBtZWFuKEYxKSwKICAgICAgICAgICAgdF9wcm9wID0gbWVhbih0X3Byb3ApKSAtPiBheV90cmFqZWN0b3J5CgpheV90cmFqZWN0b3J5ICU+JQogIGdncGxvdChhZXModF9wcm9wLCBGMSkpKwogICAgZ2VvbV9wb2ludChhZXMoZ3JvdXAgPSBpZHN0cmluZykpKwogICAgI3N0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIpKwogICAgdGhlbWVfYncoKSsKICAgIGdndGl0bGUoImRvYiA9IDE5NjQsIDE5NjUsIGxpbmVhciBtb2RlbCIpCmBgYAoKPC9kaXY+CgpFYWNoIEYxIHZhbHVlIGlzIGRpcmVjdGx5IGNvbm5lY3RlZCB0byBhbiBGMSB2YWx1ZSBhdCB0aGUgbmV4dCB0aW1lIHBvaW50LgpUaGF0IGlzLCB0aGV5IGFyZSBhdXRvLWNvcnJlbGF0ZWQuCjxkaXYgc3R5bGUgPSAid2lkdGg6ODUlO21hcmdpbi1yaWdodDphdXRvO21hcmdpbi1sZWZ0OmF1dG87Ij4KYGBge3IgZmlnLndpZHRoPTIuNSwgZmlnLmhlaWdodCA9IDEuNSwgZWNobyA9IEYsIG91dC53aWR0aCA9ICIxMDAlIn0KYXkwX2RmICU+JQogIGZpbHRlcihkb2IgJWluJSBjKDE5NjQsIDE5NjUpKSU+JQogIGdyb3VwX2J5KGlkc3RyaW5nLCBkb2IsIGdlbmRlciwgbiklPiUKICBzdW1tYXJpc2UoRjEgPSBtZWFuKEYxKSwKICAgICAgICAgICAgdF9wcm9wID0gbWVhbih0X3Byb3ApKSAtPiBheV90cmFqZWN0b3J5CgpheV90cmFqZWN0b3J5ICU+JQogIGdncGxvdChhZXModF9wcm9wLCBGMSkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGlkc3RyaW5nKSkrCiAgICAjc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIikrCiAgICB0aGVtZV9idygpKwogICAgZ2d0aXRsZSgiZG9iID0gMTk2NCwgMTk2NSwgbGluZWFyIG1vZGVsIikKYGBgCgo8L2Rpdj4KWW91IGNhbiBjYXB0dXJlIHRoaXMgd2l0aCBnYW1zIGFsc28hCgojIyMgTXVsdGlkaW1lbnNpb25hbCBTbW9vdGhzCgpUaGUgbW9zdCBmdW4gdGhpbmcgdG8gZG8gd2l0aCBnYW1zIGlzIHRvIGZpdCBtdWx0aWRpbWVuc2lvbmFsIHNtb290aHMuIApGb3IgZXhhbXBsZSwgaG93IGRvZXMgdGhlIHRyYWplY3Rvcnkgb2YgRjEgY2hhbmdlIG92ZXIgZGF0ZXMgb2YgYmlydGg/CkkndmUgaW5jbHVkZWQgdGhlIGNvZGUgZm9yIHRoYXQgbW9kZWwgaGVyZSwganVzdCBzbyB5b3UgY2FuIHNlZSBob3cgc2ltcGxlIGFuIGV4dGVuc2lvbiBpdCBpcyBvZiB3aGF0IHdlJ3ZlIGFscmVhZHkgZG9uZS4KCmBgYHtyIGV2YWwgPSBGfQpiaWdfYXlfbW9kZWwgPC0gZ2FtKEYxIH4gdGUodF9wcm9wLCBkb2IsIGsgPSAxMCkrCiAgICAgICAgICAgICAgICAgICAgICAgcyhpZHN0cmluZywgYnMgPSAncmUnKSwgCiAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBheTBfbSkKYGBgCgoKYGBge3IgZWNobyA9IEZ9CmF5X3ByZWQgPC0gcmVhZF9yZHMoImRhdGEvYXlfcHJlZC5yZHMiKQpgYGAKCmBgYHtyIGVjaG8gPSBGfQpheV9wcmVkICU+JQogIGdncGxvdChhZXModF9wcm9wLCBmaXQpKSsKICAgIGdlb21fbGluZSgpKwogICAgdHJhbnNpdGlvbl9zdGF0ZXMoZG9iKSsKICAgIHRoZW1lX21pbmltYWwoKSsKICAgIGVhc2VfYWVzKCJjdWJpYy1pbi1vdXQiKSsKICAgIGdndGl0bGUoIkRhdGUgb2YgQmlydGgge2Nsb3Nlc3Rfc3RhdGV9IikgLT4gYW5pbV8yZAojYW5pbV9zYXZlKGZpbGVuYW1lID0gImFuaW1fMmQuZ2lmIiwgYW5pbWF0aW9uID0gYW5pbV8yZCkKYGBgCgo8ZGl2IHN0eWxlID0gIndpZHRoOjg1JTttYXJnaW4tcmlnaHQ6YXV0bzttYXJnaW4tbGVmdDphdXRvOyI+CiFbXShmaWd1cmVzL2FuaW1fMmQuZ2lmKQo8L2Rpdj4KCgpbXjFdOiBJdCBoYXMgbG93IHZhcmlhbmNlLCBidXQgaGlnaCBiaWFzLgpbXjJdOiBUaGUgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyBpcyA9IGtub3RzICsgZGVncmVlIC0gMi4gVGhlICJkZWdyZWUiIG9mIHRoZSBiYXNpcyBmdW5jdGlvbnMgY29udHJvbHMgaG93IHRoZSBjdXJ2ZXMgY29ubmVjdCBzbW9vdGhseSBhdCB0aGUga25vdHMuIEEgZGVncmVlIG9mIDMgbWFrZXMgdGhlICpyYXRlIG9mIGNoYW5nZSogc21vb3RoLCBhbmQgYSBkZWdyZWUgb2YgNCBhbHNvIG1ha2VzIHRoZSAqYWNjZWxlcmF0aW9uKiBzbW9vdGguIEEgZGVncmVlIG9mIDMgb3IgNCBpcyB1c3VhbGx5IHdoYXQgeW91J2xsIHdhbnQgdG8gYmUgd29ya2luZyB3aXRoLgo=